首页 > 解决方案 > 实现一个非泛型的静态工厂方法以从字符串输入创建各种泛型类(不使用“动态”类型)

问题描述

我有一组从解析的 XML 定义和填充的类。

作为其中的一部分,我希望能够动态实例化特定类型的集合类,如 XML 指定的那样(在本例中,用于管理/实例化异常)。

所以我有一个类,大致定义如下:

public class ExceptionGroup<T> : BaseCollectionClass<Exception> where T : Exception 
{
    // contents of this class don't really matter
}

当我处理 XML 时,我将在字符串中包含异常派生类型的名称(即“ArgumentNullException”、“InvalidDataException”、“IndexOutOfRangeException”等),以及将使用的内容(消息)当/如果它被抛出时填充生成的异常(也是一个字符串)。

所以,为了到达我想要去的地方,我首先实现了几个相关的静态类(在别处定义):

// Recursively determines if a supplied Type is ultimately derived from the Exception class:
public static bool IsException( Type type ) =>
    (type == typeof( object )) ? false : (type == typeof(Exception)) || IsException( type.BaseType );

// Figures out if the specified string contains the name of a recognized Type 
// and that the Type itself is genuinely derived from the Exception base class:
public static Type DeriveType( string name )
{
    // If the string is null, empty, whitespace, or malformed, it's not valid and we ignore it...
    if ( !string.IsNullOrWhiteSpace( name ) && Regex.IsMatch( name, @"^([a-z][\w]*[a-z0-9])$", RegexOptions.IgnoreCase ) )
        try
        {
            Type excType = System.Type.GetType( name );
            if ( IsException( excType ) ) return excType;
        }
        catch { }

    // The type could not be determined, return null:
    return null; 
}

使用这些类,我可以获取一个输入字符串并最终得到一个从 Exception 类派生的已知、现有的 C# 类型类(假设输入字符串是有效的)。现在我想构建一个可以创建新ExceptionGroup<T>对象的工厂方法,其中“T”是从原始字符串派生的对象类型。

我已经通过使用类型作为工厂的返回类型dynamic管理这个,如下所示:

public static dynamic CreateExceptionGroup( string exceptionTypeName )
{
    Type excType = DeriveType( exceptionTypeName );
    if ( !(excType is null) )
    {
        Type groupType = typeof( ExceptionGroup<> ).MakeGenericType( new Type[] { excType } );
        return Activator.CreateInstance( groupType );
    }
    return null;
}

不过,我对此感到非常不舒服,既因为我不喜欢结果的模棱两可/不确定性,又因为随后处理该结果可能会更加麻烦/复杂。我宁愿更具体地指定返回类型,然后以某种方式适当地转换/限定它,例如(是的,我知道这是无效的!):

public static ExceptionGroup<> CreateGroup( string exceptionTypeName )
{
    Type excType = DeriveType( exceptionTypeName );
    if ( !(excType is null) )
    {
        Type[] types = new Type[] { excType };
        Type groupType = typeof( ExceptionGroup<> ).MakeGenericType( types );
        return (ExceptionGroup<>)Activator.CreateInstance( groupType );
    }
    return null;
}

...但是,当然ExceptionGroup<>在此语法/上下文中无效。(生成“CS7003:意外使用未绑定的通用名称”),两者都不是仅仅使用ExceptionGroup(生成:“CS0305:使用通用类型 'ExceptionGroup' 需要 1 个类型参数。”)

那么,没有办法通过其他语法或机制使用强(er)类型来做到这一点,并且可以更精确地描述结果,或者使用dynamic,以及所有后续相关的开销,实际上是实现此目的的唯一方法?

标签: c#factorybuilderstrong-typing

解决方案


虽然我希望可能有一个更简单/简洁的解决方案(如果有的话,我很乐意看到它!)Sweeper 的一些回顾性提示让我意识到我可以通过注入一个新的抽象祖先的开销来基本上绕过这个问题班级:

public abstract class ExceptionGroupFoundation : BaseCollectionClass<Exception>
{
    public ExceptionGroupFoundation( object[] args = null ) : base( args ) { }

    // Implement necessary common accessors, methods, fields, properties etc here...
    // (preferably "abstract" as/when/where possible)
}

...然后从那个派生我的通用类:

public class ExceptionGroup<T> : ExceptionGroupFoundation where T : Exception 
{
    public ExceptionGroup( object[] args = null ) : base( args ) { }

    // contents of this class don't really matter
}

...然后使用新的抽象类作为返回类型声明我的工厂方法:

public static ExceptionGroupFoundation CreateGroup( string exceptionTypeName )
{
    Type excType = DeriveType( exceptionTypeName );
    if ( !(excType is null) )
    {
        Type[] types = new Type[] { excType };
        Type groupType = typeof( ExceptionGroup<> ).MakeGenericType( types );
        return (ExceptionGroupFoundation)Activator.CreateInstance( groupType );
    }
    return null;
}

...基本上达到预期的结果,尽管有些麻烦/尴尬。


推荐阅读