在C#中转换为泛型类型

38 c# generics casting

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

typeof(LoginMessage) maps to MessageProcessor<LoginMessage>
Run Code Online (Sandbox Code Playgroud)

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

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

Type key = message.GetType();
MessageProcessor<key> processor = messageProcessors[key] as MessageProcessor<key>;
Run Code Online (Sandbox Code Playgroud)

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

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

Jer*_*ink 30

这对你有用吗?

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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


ptr*_*trc 23

以下似乎也有效,它比其他答案稍微短一些:

T result = (T)Convert.ChangeType(otherTypeObject, typeof(T));
Run Code Online (Sandbox Code Playgroud)

  • 啊,我不明白其中的区别,只知道它对我有用.谢谢你的解释. (6认同)
  • 我猜这是因为这绝对是错误的.转换不是强制转换.http://msdn.microsoft.com/en-us/library/dtb69x08.aspx只有在实现IConvertible时才有效,正如您所看到的,IConvertible只定义了对CLR类型的转换:http://msdn.microsoft.com/ EN-US /库/ system.iconvertible.aspx (4认同)
  • 如果有任何具体原因被否决,我很想知道为什么 - 这是否是不好的做法,不起作用还是其他什么? (2认同)
  • 为了解决IConvertible的问题,只需实现一个过滤类型的方法,如下所示:public T ConvertObject <T>(object sourceObject)其中T:IConvertible {return(T)Convert.ChangeType(sourceObject,typeof(T)); } (2认同)

BFr*_*ree 9

Type type = typeof(MessageProcessor<>).MakeGenericType(key);
Run Code Online (Sandbox Code Playgroud)

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

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

object obj = Activator.CreateInstance(type);
Run Code Online (Sandbox Code Playgroud)

OBJ现在将正确的类型,但是因为你不知道什么类型的"钥匙"是在编译的时候,有没有办法施展它,做任何有用的事情.


Dan*_*ted 8

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

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

    //  Call method processor or whatever you need to do
}
Run Code Online (Sandbox Code Playgroud)

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

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});
}
Run Code Online (Sandbox Code Playgroud)

  • 为了优化这一点,您可以将其设置为每种消息类型仅进行一次反射。您可以创建一个调用泛型方法的委托并将其存储在字典中,以便下次出现该类型时它只调用该委托。最简单的方法可能是编译一个 lambda 表达式。 (2认同)

Cas*_*rns 8

我遇到了类似的问题.我上课了;

Action<T>
Run Code Online (Sandbox Code Playgroud)

它具有T型属性

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

解:

实现非通用接口;

public interface IGetGenericTypeInstance
{
    object GenericTypeInstance();
}
Run Code Online (Sandbox Code Playgroud)

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


小智 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();
    }
}
Run Code Online (Sandbox Code Playgroud)