C# 获取从抽象类继承并在属性中具有特定值的类的实例

C# 获取从抽象类继承并在属性中具有特定值的类的实例,c#,reflection,C#,Reflection,我正在一家工厂工作,根据两个标准创建具体实例: 1) 该类必须从特定的抽象类继承 2) 该类在重写的属性中必须具有特定值 我的代码如下所示: public abstract class CommandBase { public abstract string Prefix { get; } } public class PaintCommand : CommandBase { public override string Prefix { get; } = "P"; } pu

我正在一家工厂工作,根据两个标准创建具体实例:

1) 该类必须从特定的抽象类继承

2) 该类在重写的属性中必须具有特定值

我的代码如下所示:

public abstract class CommandBase
{
    public abstract string Prefix { get; }
}

public class PaintCommand : CommandBase
{
    public override string Prefix { get; } = "P";
}

public class WalkCommand : CommandBase
{
    public override string Prefix { get; } = "W";
}

class Program
{
    static void Main(string[] args)
    {
        var paintCommand = GetInstance("P");
        var walkCommand = GetInstance("W");  

        Console.ReadKey();
    }

    static CommandBase GetInstance(string prefix)
    {
        try
        {            
            var currentAssembly = Assembly.GetExecutingAssembly();
            var concreteType = currentAssembly.GetTypes().Where(t => t.IsSubclassOf(typeof(CommandBase)) &&
                                                                     !t.IsAbstract &&
                                                                     t.GetProperty("Prefix").GetValue(t).ToString() == prefix).SingleOrDefault();

            if (concreteType == null)
                throw new InvalidCastException($"No concrete type found for command: {prefix}");

            return (CommandBase)Activator.CreateInstance(concreteType);
        }
        catch (Exception ex)
        {            
            return default(CommandBase);
        }
    }
}
我得到了一个错误:

{System.Reflection.TargetException:对象与System.Reflection.RuntimeMethodInfo.CheckConsistency(对象目标)at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck的目标类型不匹配(对象obj,BindingFlags invokeAttr,绑定器绑定器,对象[]参数,CultureInfo区域性)在System.Reflection.RuntimeMethodInfo.Invoke(对象obj、BindingFlags invokeAttr、绑定器绑定器、对象[]参数、CultureInfo区域性)


更简洁的方法是定义自己的属性来存储前缀

[AttributeUsage(AttributeTargets.Class)]
public class CommandAttribute : Attribute
{
    public String Prefix { get; set; }

    public CommandAttribute(string commandPrefix)
    {
        Prefix = commandPrefix;
    }
}
然后像这样使用它们:

[CommandAttribute("P")]
public class PaintCommand : CommandBase
{}

[CommandAttribute("W")]
public class WalkCommand : CommandBase
{}
反思:

static CommandBase GetInstance(string prefix)
{       
    var currentAssembly = Assembly.GetExecutingAssembly();
    var concreteType = currentAssembly.GetTypes().Where(commandClass => commandClass.IsDefined(typeof(CommandAttribute), false) && commandClass.GetCustomAttribute<CommandAttribute>().Prefix == prefix).FirstOrDefault();

    if (concreteType == null)
        throw new InvalidCastException($"No concrete type found for command: {prefix}");

    return (CommandBase)Activator.CreateInstance(concreteType);
}
static CommandBase GetInstance(字符串前缀)
{       
var currentAssembly=Assembly.getExecutionGassembly();
var concreteType=currentAssembly.GetTypes().Where(commandClass=>commandClass.IsDefined(typeof(CommandAttribute),false)和&commandClass.GetCustomAttribute().Prefix==Prefix.FirstOrDefault();
if(concreteType==null)
抛出新的InvalidCastException($“未找到命令的具体类型:{prefix}”);
return(CommandBase)Activator.CreateInstance(concreteType);
}

正如spender在其评论中所提到的,您得到此特定错误的原因如下:

t.GetProperty("Prefix").GetValue(t)
这里,
t
是一个包含类的类型变量,例如
WalkCommand
。您将为该类的
Prefix
属性获取
PropertyInfo
对象,然后尝试使用
GetValue()
WalkCommand
对象的实例读取该属性的值

问题是,您没有传递
GetValue()
WalkCommand类的实例,而是传递了
类型
,因此反射会引发此异常

有几种方法可以解决这个问题:

1) 动态创建每种类型的实例,只是读取其前缀(我真的不建议这样做):

2) 将整个过程更改为使用属性,例如SwiftingDuster的答案中的属性

3) 使用静态构造函数创建一个字典,将字符串前缀映射到具体类型。反射代价很高,并且类不会更改(除非动态加载程序集),所以请执行一次

我们可以通过(ab)使用我之前的“创建实例以丢弃”代码来实现这一点,因此我们仍然创建每个类的实例只是为了读取属性,但至少我们只执行一次:

static Dictionary<string, Type> prefixMapping;

static Program()
{
    prefixMapping = currentAssembly.GetTypes()
        .Where(t => t.IsSubclassOf(typeof(CommandBase)) && !t.IsAbstract)
        .Select(t => new { t, c = (CommandBase)Activator.CreateInstance(t) })
        .ToDictionary(x => x.t.GetProperty("Prefix").GetValue(x.c).ToString(), x => x.t);
}

static CommandBase GetInstance(string prefix)
{
    Type concreteType;
    if ( prefixMapping.TryGetValue(prefix, out concreteType) )
    {
        return (CommandBase)Activator.CreateInstance(concreteType);
    }
    return default(CommandBase);
}

希望这有助于

所以要读取类实例属性,需要向方法
t.GetProperty(“Prefix”).GetValue(someInstance)提供该实例的对象
。显然,实例化每种类型的实例只是为了测试它是多余的。在我看来,静态类属性而不是重写的实例属性可能更适合您的反射代码。考虑到您是通过字符串查找的,(除非它有另一个未记录的用途),前缀的继承性质不会带来任何好处。
static Dictionary<string, Type> prefixMapping;

static Program()
{
    prefixMapping = currentAssembly.GetTypes()
        .Where(t => t.IsSubclassOf(typeof(CommandBase)) && !t.IsAbstract)
        .Select(t => new { t, c = (CommandBase)Activator.CreateInstance(t) })
        .ToDictionary(x => x.t.GetProperty("Prefix").GetValue(x.c).ToString(), x => x.t);
}

static CommandBase GetInstance(string prefix)
{
    Type concreteType;
    if ( prefixMapping.TryGetValue(prefix, out concreteType) )
    {
        return (CommandBase)Activator.CreateInstance(concreteType);
    }
    return default(CommandBase);
}
static Dictionary<string, Type> prefixMapping;

static Program()
{
    prefixMapping = currentAssembly.GetTypes()
        .Where(t => t.IsSubclassOf(typeof(CommandBase)) && t.IsDefined(typeof(CommandAttribute), false) && !t.IsAbstract)
        .Select(t => new { t, p = t.GetCustomAttribute<CommandAttribute>().Prefix })
        .GroupBy(x => x.p)
        .ToDictionary(g => g.Key, g => g.First().t);
}

static CommandBase GetInstance(string prefix)
{
    Type concreteType;
    if ( prefixMapping.TryGetValue(prefix, out concreteType) )
    {
        return (CommandBase)Activator.CreateInstance(concreteType);
    }
    return default(CommandBase);
}