简体   繁体   中英

Reflection: Get Class name from supplied property value

I have a base class with a number of child classes. Each class have a GetType property that will return a specified enum value. Is it possible to get the class name when I supply the enum value?

Example:

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.
}

Edit: A little background. I have a Message parent class that will process messages in a queue. Each 'kind-of' message subclass will override a ProcessMessage function.

I also have Queue class that will iterate through the Queue and process each message. So the "correct" option will probably be to explicitly instantiate an object of each child class in the Queue class and call ChildClass.ProcessMessage, but to minimise maintenance with new MessageTypes added, I want to "read" the name of the child class and instantiate an object from there. This will then prevent extra if- and switch statements when new Message types are added.

You're going to have to scan the Assembly for all the types that inherit from ParentClass , then create the instance of each derived class, call the method to get the EnumType value and find the subclass that corresponds to the searched value. Assuming that all derived types are in the same assembly - otherwise you will have to scan all the assemblies/known assemblies.

Elaborate a little on what you are trying to achieve, surely there is a better way of doing that then this.

I wouldn't create a method for that, as you need an instance to call that method. You could however create a static property which returns the type. Maybe an attribute would even be better.

I created a fiddle as an example: https://dotnetfiddle.net/IweLy7

It relies on scanning the assembly for relevant types. As stated by the other answer you may have to check additional assemblies for your usecase.

According to your edited question, I'll give you a basic idea.

Suppose, that there are message types:

// Message types
class Message { }
class MessageA : Message { }
class MessageB : Message { }

and base message processor:

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);
    }
}

You need to bind particular message type to its custom processor. This could be done using some metadata, that is, you need custom attribute:

// Mapping attribute
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
class MessageProcessorAttribute : Attribute
{
    public MessageProcessorAttribute(Type messageType)
    {
        MessageType = messageType;
    }

    public Type MessageType { get; }
}

Now you can declare message processors:

[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...");
    }
}

Here's sample queue, that will use code from above:

// 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)
        };
    }
}

You can combine this approach with DI-containers and build plugin-based applications - number of message types and processors, as their location (same assembly or several assemblies) doesn't matter.

UPDATE . I've added some generics to avoid type casting in every descendant.

You can get the type of an object with the GetType().ToString() method, and then convert it to an enum.

public override EnumClassNames MyType()
{
    string stringifiedType = this.GetType().ToString();

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

    return myEnumType;
}

You can also use the Enum.TryParse() method if you are not sure of the type you are trying to convert.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM