简体   繁体   English

在 C# 中转换为泛型

[英]Cast to generic type in C#

I have a Dictionary to map a certain type to a certain generic object for that type.我有一个字典到 map 某种类型到某种通用 object 该类型。 For example:例如:

typeof(LoginMessage) maps to MessageProcessor<LoginMessage>

Now the problem is to retrieve this generic object at runtime from the Dictionary.现在的问题是在运行时从字典中检索这个通用的 object。 Or to be more specific: To cast the retrieved object to the specific generic type.或者更具体地说:将检索到的 object 转换为特定的泛型类型。

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.编辑:我不想使用 Ifs 和开关。 Due to performance issues I cannot use reflection of some sort either.由于性能问题,我也不能使用某种反射。

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

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.这为您提供了正确的 object。我唯一不确定的是在您的情况下是否足以将其作为抽象 MessageProcessor。

Edit: I added an IMessage interface.编辑:我添加了一个 IMessage 界面。 The actual processing code should now become part of the different message classes that should all implement this interface.实际的处理代码现在应该成为不同消息类的一部分,这些消息类都应该实现这个接口。

I had a similar problem.我有一个类似的问题。 I have a class;我有一个 class;

Action<T>

which has a property of type T.它具有类型 T 的属性。

How do I get the property when I don't know T?当我不认识 T 时,我如何获得财产? I can't cast to Action<> unless I know 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.现在我可以将 object 转换为 IGetGenericTypeInstance 并且 GenericTypeInstance 将返回类型为 object 的属性。

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.我从 var 类型更改为 Type 类型。 My point is, now you can do something like this:我的意思是,现在你可以这样做:

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”是什么类型,因此无法转换它并用它做任何有用的事情。

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

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

You can't do that.你不能那样做。 You could try telling your problem from a more high level point of view (ie what exactly do you want to accomplish with the casted variable) for a different solution.对于不同的解决方案,您可以尝试从更高层次的角度(即您究竟想用转换变量完成什么)来讲述您的问题。

You could go with something like this:你可以 go 像这样:

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

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

To convert any type object to a generic type T , the trick is to first assign to an object of any higher type then cast that to the generic type.要将任何类型 object 转换为通用类型T ,诀窍是首先分配给任何更高类型的 object 然后将其转换为通用类型。

object temp = otherTypeObject;
T result = (T)temp;

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.使用反射,您可以将映射的 object 传递给任何需要它的方法,然后将为您执行转换。 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 作为参数,那么您可以找到它并动态调用它。

    public delegate void MessageProcessor<T>(T msg) where T : IExternalizable;


    virtual public void OnRecivedMessage(IExternalizable msg)
    {
        Type type = msg.GetType();
        ArrayList list = processors.Get(type);
        if (list != null)
        {
            object[] args = new object[]{msg};
            for (int i = list.Count - 1; i >= 0; --i)
            {
                Delegate e = (Delegate)list[i];
                e.Method.Invoke(e.Target, args);
            }
        }
    }

The answer of @DanielPlaisted before generally works, but the generic method must be public or one must use BindingFlags.NonPublic | BindingFlags.Instance @DanielPlaisted 之前的回答一般有效,但泛型方法必须是公共的或者必须使用BindingFlags.NonPublic | BindingFlags.Instance BindingFlags.NonPublic | BindingFlags.Instance . BindingFlags.NonPublic | BindingFlags.Instance .实例。 Couldn't post it as a comment for lack of reputation.由于缺乏声誉,无法将其作为评论发布。

I struggled to solve a similar problem around data.table classes instead of messages.我努力解决围绕 data.table 类而不是消息的类似问题。 The root issue mentioned above of casting a non-generic version of the class to a derived generic version was the same.上面提到的将 class 的非通用版本转换为派生的通用版本的根本问题是相同的。

In order to allow injection into a portable class library which did not support database libraries, I introduced a set of interface classes, with the intent that I could pass a type and get a matching generic.为了允许注入不支持数据库库的可移植 class 库,我引入了一组接口类,目的是我可以传递一个类型并获得匹配的泛型。 It ended up needing to implement a generic method.它最终需要实现一个通用方法。

// Interface for injection
public interface IDatabase
{
    // Original, non-functional signature:
    IDatatable<object> GetDataTable(Type dataType);

    // Functional method using a generic method:
    IDatatable<T> GetDataTable<T>();
}

And this the whole implementation using the generic method above.这是使用上面的通用方法的整个实现。

The generic class that will be cast from a dictionary.将从字典中转换的通用 class。

// Non-generic base class allows listing tables together
abstract class Datatable
{
    Datatable(Type storedClass)
    {
      StoredClass = storedClass;
    }

    Type StoredClass { get; private set; }
}

// Generic inheriting class
abstract class Datatable<T>: Datatable, IDatatable<T>
{
    protected Datatable()
        :base(typeof(T))
    {
    }
}

This is the class that stores the generic class and casts it to satisfy the generic method in the interface这个就是存储泛型class的class,并强制转换以满足接口中的泛型方法

class Database
{
    // Dictionary storing the classes using the non-generic base class
    private Dictionary<Type, Datatable> _tableDictionary;

    protected Database(List<Datatable> tables)
    {
        _tableDictionary = new Dictionary<Type, Datatable>();
        foreach (var table in tables)
        {
            _tableDictionary.Add(table.StoredClass, table);
        }
    }

    // Interface implementation, casts the generic
    public IDatatable<T> GetDataTable<T>()
    {
        Datatable table = null;

        _tableDictionary.TryGetValue(typeof(T), out table);

        return table as IDatatable<T>;
    }
}

And finally the calling of the interface method.最后是接口方法的调用。

IDatatable<CustomerAccount> table = _database.GetDataTable<CustomerAccount>();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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