在C#中转换为泛型类型

时间:2009-06-16 18:02:04

标签: c# generics casting

我有一个Dictionary,用于将某种类型映射到该类型的某个通用对象。例如:

typeof(LoginMessage) maps to MessageProcessor<LoginMessage>

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

我需要它来做这样的事情:

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

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

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

13 个答案:

答案 0 :(得分:29)

这对你有用吗?

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

这为您提供了正确的对象。我唯一不确定的是你的情况是否足以将它作为一个抽象的MessageProcessor。

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

答案 1 :(得分:19)

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

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

答案 2 :(得分:9)

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

这是你能做到的最好的事情,但是如果不知道它是什么类型的话,你真的可以用它做更多的事情。

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

object obj = Activator.CreateInstance(type);

obj现在是正确的类型,但由于你不知道编译时“键”是什么类型,所以没有办法强制转换它并对它做任何有用的事情。

答案 3 :(得分:8)

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

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

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

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

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

答案 4 :(得分:8)

我有类似的问题。我有一个班级;

Action<T>

具有T类型的属性。

当我不知道T时如何获得财产?我无法投射到Action&lt;&gt;除非我知道T。

<强> SOLUTION:

实施非通用接口;

public interface IGetGenericTypeInstance
{
    object GenericTypeInstance();
}

现在我可以将对象强制转换为IGetGenericTypeInstance,GenericTypeInstance将该属性作为类型对象返回。

答案 5 :(得分:5)

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

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

答案 6 :(得分:4)

你做不到。对于不同的解决方案,您可以尝试从更高级别的角度讲述问题(即,您希望使用转换变量完成什么)。

你可以选择这样的东西:

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

答案 7 :(得分:3)

这是不允许的:

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

您无法将泛型类型作为变量值。

你必须做一个开关或其他事情:

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

答案 8 :(得分:1)

如上所述,您无法直接投射它。一种可能的解决方案是让这些泛型类型继承自非泛型接口,在这种情况下,您仍然可以在其上调用方法而不进行反射。使用反射,您可以将映射对象传递给任何期望它的方法,然后将为您执行强制转换。所以,如果你有一个名为Accept expecting a的方法 MessageProcessor作为参数,然后您可以找到它并动态调用它。

答案 9 :(得分:1)

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

答案 10 :(得分:0)

@DanielPlaisted之前的答案通常有效,但通用方法必须是公开的,或者必须使用BindingFlags.NonPublic | BindingFlags.Instance!无法将其作为缺乏声誉的评论发布。

答案 11 :(得分:0)

我努力解决围绕数据表类而不是消息的类似问题。上面提到的将类的非泛型版本转换为派生泛型版本的根本问题是相同的。

为了允许注入不支持数据库库的可移植类库,我引入了一组接口类,目的是我可以传递一个类型并获得匹配的泛型。它最终需要实现一个通用的方法。

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

这是使用上述通用方法的整个实现。

将从字典中强制转换的泛型类。

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

这是存储泛型类并将其强制转换为满足接口

中的泛型方法的类
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>;
    }
}

最后调用接口方法。

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

答案 12 :(得分:0)

要将任何类型的对象转换为泛型T,诀窍是首先将其分配给任何更高类型的对象,然后将其转换为泛型。

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