在 C# 中转换为泛型类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1003023/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 05:16:52  来源:igfitidea点击:

Cast to generic type in C#

c#genericscasting

提问by

I have a Dictionary to map a certain type to a certain generic object for that type. For example:

我有一个字典来将某个类型映射到该类型的某个通用对象。例如:

typeof(LoginMessage) maps to MessageProcessor<LoginMessage>

Now the problem is to retrieve this generic object at runtime from the Dictionary. Or to be more specific: To cast the retrieved object to the specific generic type.

现在的问题是在运行时从字典中检索这个通用对象。或者更具体地说:将检索到的对象转换为特定的泛型类型。

I need it to work something like this:

我需要它像这样工作:

Type key = message.GetType();
MessageProcessor<key> processor = messageProcessors[key] as MessageProcessor<key>;

Hope there is a easy solution to this.

希望有一个简单的解决方案。

Edit: I do not want to use Ifs and switches. Due to performance issues I cannot use reflection of some sort either.

编辑:我不想使用 Ifs 和开关。由于性能问题,我也不能使用某种反射。

采纳答案by Jeroen Huinink

Does this work for you?

这对你有用吗?

interface IMessage
{
    void Process(object source);
}

class LoginMessage : IMessage
{
    public void Process(object source)
    {
    }
}

abstract class MessageProcessor
{
    public abstract void ProcessMessage(object source, object type);
}

class MessageProcessor<T> : MessageProcessor where T: IMessage
{
    public override void ProcessMessage(object source, object o) 
    {
        if (!(o is T)) {
            throw new NotImplementedException();
        }
        ProcessMessage(source, (T)o);
    }

    public void ProcessMessage(object source, T type)
    {
        type.Process(source);
    }
}


class Program
{
    static void Main(string[] args)
    {
        Dictionary<Type, MessageProcessor> messageProcessors = new Dictionary<Type, MessageProcessor>();
        messageProcessors.Add(typeof(string), new MessageProcessor<LoginMessage>());
        LoginMessage message = new LoginMessage();
        Type key = message.GetType();
        MessageProcessor processor = messageProcessors[key];
        object source = null;
        processor.ProcessMessage(source, message);
    }
}

This gives you the correct object. The only thing I am not sure about is whether it is enough in your case to have it as an abstract MessageProcessor.

这为您提供了正确的对象。我唯一不确定的是,在您的情况下,将它作为抽象 MessageProcessor 是否足够。

Edit: I added an IMessage interface. The actual processing code should now become part of the different message classes that should all implement this interface.

编辑:我添加了一个 IMessage 接口。实际的处理代码现在应该成为应该都实现这个接口的不同消息类的一部分。

回答by Mehrdad Afshari

You can't do that. You could try telling your problem from a more high level point of view (i.e. what exactly do you want to accomplish with the casted variable) for a different solution.

你不能那样做。您可以尝试从更高层次的角度(即您想用强制转换的变量完成什么)来告诉您的问题,以获得不同的解决方案。

You could go with something like this:

你可以用这样的东西:

 public abstract class Message { 
     // ...
 }
 public class Message<T> : Message {
 }

 public abstract class MessageProcessor {
     public abstract void ProcessMessage(Message msg);
 }
 public class SayMessageProcessor : MessageProcessor {
     public override void ProcessMessage(Message msg) {
         ProcessMessage((Message<Say>)msg);
     }
     public void ProcessMessage(Message<Say> msg) {
         // do the actual processing
     }
 }

 // Dispatcher logic:
 Dictionary<Type, MessageProcessor> messageProcessors = {
    { typeof(Say), new SayMessageProcessor() },
    { typeof(string), new StringMessageProcessor() }
 }; // properly initialized

 messageProcessors[msg.GetType().GetGenericArguments()[0]].ProcessMessage(msg);

回答by BFree

Type type = typeof(MessageProcessor<>).MakeGenericType(key);

That's the best you can do, however without actually knowing what type it is, there's really not much more you can do with it.

这是你能做的最好的事情,但是如果你不知道它是什么类型,你真的无能为力。

EDIT:I should clarify. I changed from var type to Type type. My point is, now you can do something like this:

编辑:我应该澄清一下。我从 var 类型更改为 Type 类型。我的观点是,现在你可以做这样的事情:

object obj = Activator.CreateInstance(type);

obj will now be the correct type, but since you don't know what type "key" is at compile time, there's no way to cast it and do anything useful with it.

obj 现在将是正确的类型,但是由于您在编译时不知道“key”是什么类型,因此无法对其进行强制转换并对其进行任何有用的操作。

回答by eulerfx

As mentioned, you cannot cast it directly. One possible solution is to have those generic types inherit from a non-generic interface, in which case you can still invoke methods on it without reflection. Using reflection, you can pass the mapped object to any method expecting it, then the cast will be performed for you. So if you have a method called Accept expecting a MessageProcessor as a parameter, then you can find it and invoke it dynamically.

如前所述,您不能直接投射它。一种可能的解决方案是让这些泛型类型从非泛型接口继承,在这种情况下,您仍然可以在没有反射的情况下调用它的方法。使用反射,您可以将映射对象传递给任何需要它的方法,然后将为您执行转换。因此,如果您有一个名为 Accept 的方法需要一个 MessageProcessor 作为参数,那么您可以找到它并动态调用它。

回答by Colin Burnett

This is simply not allowed:

这是根本不允许的:

Type key = message.GetType();
MessageProcessor<key> processor = messageProcessors[key] as MessageProcessor<key>;

You cannot get a generic type as a variable value.

您不能将泛型类型作为变量值。

You'd have to do a switch or something:

你必须做一个开关或什么:

Type key = message.GetType();
if (key == typeof(Foo))
{
    MessageProcessor<Foo> processor = (MessageProcessor<Foo>)messageProcessors[key];
    // Do stuff with processor
}
else if (key == typeof(Bar))
{
    MessageProcessor<bar> processor = (MessageProcessor<Bar>)messageProcessors[key];
    // Do stuff with processor
}
...

回答by Daniel Plaisted

You can write a method that takes the type as a generic parameter:

您可以编写一个将类型作为泛型参数的方法:

void GenericProcessMessage<T>(T message)
{
    MessageProcessor<T> processor = messageProcessors[typeof(T)]
        as MessageProcessor<T>;

    //  Call method processor or whatever you need to do
}

Then you need a way to call the method with the correct generic argument. You can do this with reflection:

然后您需要一种方法来使用正确的泛型参数调用该方法。你可以用反射来做到这一点:

public void ProcessMessage(object message)
{
    Type messageType = message.GetType();
    MethodInfo method = this.GetType().GetMethod("GenericProcessMessage");
    MethodInfo closedMethod = method.MakeGenericMethod(messageType);
    closedMethod.Invoke(this, new object[] {message});
}

回答by Prakash Buddhiraja

Please see if following solution works for you. The trick is to define a base processor interface which takes a base type of message.

请查看以下解决方案是否适合您。诀窍是定义一个基本处理器接口,它采用基本类型的消息。

interface IMessage
{
}

class LoginMessage : IMessage
{
}

class LogoutMessage : IMessage
{
}

class UnknownMessage : IMessage
{
}

interface IMessageProcessor
{
    void PrcessMessageBase(IMessage msg);
}

abstract class MessageProcessor<T> : IMessageProcessor where T : IMessage
{
    public void PrcessMessageBase(IMessage msg)
    {
        ProcessMessage((T)msg);
    }

    public abstract void ProcessMessage(T msg);

}

class LoginMessageProcessor : MessageProcessor<LoginMessage>
{
    public override void ProcessMessage(LoginMessage msg)
    {
        System.Console.WriteLine("Handled by LoginMsgProcessor");
    }
}

class LogoutMessageProcessor : MessageProcessor<LogoutMessage>
{
    public override void ProcessMessage(LogoutMessage msg)
    {
        System.Console.WriteLine("Handled by LogoutMsgProcessor");
    }
}

class MessageProcessorTest
{
    /// <summary>
    /// IMessage Type and the IMessageProcessor which would process that type.
    /// It can be further optimized by keeping IMessage type hashcode
    /// </summary>
    private Dictionary<Type, IMessageProcessor> msgProcessors = 
                                new Dictionary<Type, IMessageProcessor>();
    bool processorsLoaded = false;

    public void EnsureProcessorsLoaded()
    {
        if(!processorsLoaded)
        {
            var processors =
                from processorType in Assembly.GetExecutingAssembly().GetTypes()
                where processorType.IsClass && !processorType.IsAbstract &&
                      processorType.GetInterface(typeof(IMessageProcessor).Name) != null
                select Activator.CreateInstance(processorType);

            foreach (IMessageProcessor msgProcessor in processors)
            {
                MethodInfo processMethod = msgProcessor.GetType().GetMethod("ProcessMessage");
                msgProcessors.Add(processMethod.GetParameters()[0].ParameterType, msgProcessor);
            }

            processorsLoaded = true;
        }
    }

    public void ProcessMessages()
    {
        List<IMessage> msgList = new List<IMessage>();
        msgList.Add(new LoginMessage());
        msgList.Add(new LogoutMessage());
        msgList.Add(new UnknownMessage());

        foreach (IMessage msg in msgList)
        {
            ProcessMessage(msg);
        }
    }

    public void ProcessMessage(IMessage msg)
    {
        EnsureProcessorsLoaded();
        IMessageProcessor msgProcessor = null;
        if(msgProcessors.TryGetValue(msg.GetType(), out msgProcessor))
        {
            msgProcessor.PrcessMessageBase(msg);
        }
        else
        {
            System.Console.WriteLine("Processor not found");
        }
    }

    public static void Test()
    {
        new MessageProcessorTest().ProcessMessages();
    }
}

回答by Casey Burns

I had a similar problem. I have a class;

我有一个类似的问题。我有一堂课;

Action<T>

which has a property of type T.

它具有类型 T 的属性。

How do I get the property when I don't know T? I can't cast to Action<> unless I know T.

当我不知道 T 时,我如何获得财产?除非我知道 T,否则我无法转换到 Action<>。

SOLUTION:

解决方案:

Implement a non-generic interface;

实现非通用接口;

public interface IGetGenericTypeInstance
{
    object GenericTypeInstance();
}

Now I can cast the object to IGetGenericTypeInstance and GenericTypeInstance will return the property as type object.

现在我可以将对象转换为 IGetGenericTypeInstance 并且 GenericTypeInstance 将属性作为类型对象返回。

回答by ptrc

The following seems to work as well, and it's a little bit shorter than the other answers:

以下似乎也有效,并且比其他答案短一点:

T result = (T)Convert.ChangeType(otherTypeObject, typeof(T));

回答by Peter

The answer of @DanielPlaisted before generally works, but the generic method must be public or one must use BindingFlags.NonPublic | BindingFlags.Instance! Couldn't post it as a comment for lack of reputation.

之前@DanielPlaisted 的回答一般有效,但泛型方法必须是公共的,或者必须使用BindingFlags.NonPublic | BindingFlags.Instance!由于缺乏声誉,无法将其发布为评论。