反射:从提供的属性值中获取类名

Posted

技术标签:

【中文标题】反射:从提供的属性值中获取类名【英文标题】:Reflection: Get Class name from supplied property value 【发布时间】:2017-01-19 16:29:33 【问题描述】:

我有一个带有许多子类的基类。 每个类都有一个 GetType 属性,该属性将返回一个指定的枚举值。 提供枚举值时是否可以获取类名?

例子:

public enum EnumClassNames

    Class1 = 1,
    Class2 = 2,
 

class MySubClass : ParentClass

    public override EnumType MyType()
    
        return EnumType.Class1;
    
 

void main()
 
   var className = xxxx(EnumType.Class1); //I would like to get the value MySubClass back here.

编辑: 一点背景。我有一个 Message 父类,它将处理队列中的消息。每个“种类”的消息子类都将覆盖一个 ProcessMessage 函数。

我还有 Queue 类,它将遍历 Queue 并处理每条消息。因此,“正确”选项可能是显式实例化 Queue 类中每个子类的对象并调用 ChildClass.ProcessMessage,但是为了尽量减少添加新 MessageTypes 的维护,我想“读取”子类的名称和从那里实例化一个对象。当添加新的消息类型时,这将防止额外的 if- 和 switch 语句。

【问题讨论】:

简单的回答:你不能这样做。您要解决的实际问题是什么? ParentClass 有什么意义?你想得到那个类型,还是子类型? gobes:就这个问题而言,父类确实没有做太多事情:) 可能已经忽略了。 添加了一些额外的背景信息。 【参考方案1】:

我不会为此创建方法,因为您需要一个实例来调用该方法。但是,您可以创建一个返回类型的静态属性。也许一个属性会更好。

我创建了一个小提琴作为例子:https://dotnetfiddle.net/IweLy7

它依赖于扫描程序集以查找相关类型。正如其他答案所述,您可能需要为您的用例检查其他程序集。

【讨论】:

您描述的静态属性的唯一问题是在编译期间无法强制此类属性的存在,因此如果派生类不包含该属性,它将在第 39 行失败。 这确实是个问题。但是对于返回枚举值的方法,您需要创建一个实例。【参考方案2】:

您将不得不扫描Assembly 以查找从ParentClass 继承的所有类型,然后创建每个派生类的实例,调用方法以获取EnumType 值并找到子类对应于搜索的值。假设所有派生类型都在同一个程序集中 - 否则您将不得不扫描所有程序集/已知程序集。

详细说明您要实现的目标,肯定有比这个更好的方法。

【讨论】:

我将通过对问题的编辑进行详细说明。 如果您的应用程序中有一个 IoC 容器,您可以使用键控注册并将容器用作消息的工厂。其他选择是将消息类型的静态字典存储在父类中,并从每个消息类型中填充它,这实际上会反转过程(子类将负责暴露自己),但这不是最佳解决方案。跨度> 【参考方案3】:

根据你编辑的问题,我会给你一个基本的想法。

假设有消息类型:

// Message types
class Message  
class MessageA : Message  
class MessageB : Message  

和基本消息处理器:

interface IMessageProcessor

    void ProcessMessage(Message message);


abstract class MessageProcessor<TMessage> : IMessageProcessor
    where TMessage : Message

    protected abstract void ProcessMessage(TMessage message);

    public void ProcessMessage(Message message)
    
        ProcessMessage((TMessage)message);
    

您需要将特定的消息类型绑定到其自定义处理器。这可以使用一些元数据来完成,也就是说,您需要自定义属性:

// Mapping attribute
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
class MessageProcessorAttribute : Attribute

    public MessageProcessorAttribute(Type messageType)
    
        MessageType = messageType;
    

    public Type MessageType  get; 

现在您可以声明消息处理器了:

[MessageProcessor(typeof(MessageA))]
class MessageAProcessor : MessageProcessor<MessageA>

    protected override void ProcessMessage(MessageA message)
    
        Console.WriteLine("Processing A message...");
    


[MessageProcessor(typeof(MessageB))]
class MessageBProcessor : MessageProcessor<MessageB>

    protected override void ProcessMessage(MessageB message)
    
        Console.WriteLine("Processing B message...");
    

这是示例队列,它将使用上面的代码:

// Message queue
class MessageQueue

    private readonly Queue<Message> messages;

    public MessageQueue()
    
        messages = new Queue<Message>();
    

    public void PostMessage(Message message)
    
        messages.Enqueue(message);
    

    public void ProcessMessages()
    
        while (messages.Any())
        
            var message = messages.Dequeue();
            var messageProcessor = GetMessageProcessorOrDefault(message);

            messageProcessor?.ProcessMessage(message);
        
    

    private IMessageProcessor GetMessageProcessorOrDefault(Message message)
    
        var messageType = message.GetType();
        var messageProcessorTypes = GetMessageProcessorTypes();
        var messageProcessorType = messageProcessorTypes
            .FirstOrDefault(mpt => mpt.GetCustomAttribute<MessageProcessorAttribute>()?.MessageType == messageType);

        return messageProcessorType != null ? (IMessageProcessor)Activator.CreateInstance(messageProcessorType) : null;
    

    private IEnumerable<Type> GetMessageProcessorTypes()
    
        // TODO: scan assemblies to retrieve IMessageProcessor implementations
        return new[]
        
            typeof(MessageAProcessor),
            typeof(MessageBProcessor)
        ;
    

您可以将此方法与 DI 容器相结合,并构建基于插件的应用程序 - 消息类型和处理器的数量,因为它们的位置(相同的程序集或多个程序集)并不重要。

更新。我添加了一些泛型以避免在每个后代中进行类型转换。

【讨论】:

啊,哇,丹尼斯。竖起双大拇指。我希望我能接受超过 1 个答案,但我今天将接受 heinzbeinz 的答案。它更适合我一点。但是遇到类似问题的任何其他人都可以真正了解这两种解决方案。【参考方案4】:

您可以通过GetType().ToString()方法获取对象的类型,然后将其转换为枚举。

public override EnumClassNames MyType()

    string stringifiedType = this.GetType().ToString();

    var myEnumType = (EnumClassNames)Enum.Parse(typeof(EnumClassNames), stringifiedType);

    return myEnumType;

如果您不确定要转换的类型,也可以使用Enum.TryParse() 方法。

【讨论】:

以上是关于反射:从提供的属性值中获取类名的主要内容,如果未能解决你的问题,请参考以下文章

java如何通过反射获取包中所有的类?

反射机制--获取类的方法

day27(反射之内省机制)

通过反射获取类的的结构信息

Java 反射

Java反射 在只知道类名的情况下,怎样给其中的私有属性赋值