Приведение в общий тип в C #

У меня есть словарь для сопоставления определенного типа с определенным общим объектом для этого типа. Например:

typeof(LoginMessage) maps to MessageProcessor<LoginMessage>

Теперь проблема состоит в том, чтобы получить этот универсальный объект во время выполнения из Словаря. Или, если быть более конкретным: отбросить полученный объект к определенному родовому типу.

Мне нужно, чтобы он работал примерно так:

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

Надеюсь, что это простое решение.

Изменить: я не хочу использовать Ifs и switch. Из-за проблем с производительностью я тоже не могу использовать какое-либо отражение.

c#,generics,casting,

34

Ответов: 12


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. Фактический код обработки теперь должен стать частью различных классов сообщений, которые должны реализовать весь этот интерфейс.


15

Следующее, похоже, работает, и это немного короче, чем другие ответы:

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

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

Это лучшее, что вы можете сделать, однако, не зная, какого типа это, вы действительно можете сделать гораздо больше.

EDIT: Я должен уточнить. Я изменил с типа var на тип типа. Я хочу сказать, теперь вы можете сделать что-то вроде этого:

object obj = Activator.CreateInstance(type);

obj теперь будет правильным типом, но так как вы не знаете, какой тип «ключ» находится во время компиляции, его невозможно отбросить и сделать с ним что-нибудь полезное.


Вы можете написать метод, который принимает тип как общий параметр:

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

6

У меня была аналогичная проблема. У меня есть класс;

Action<T>

который имеет свойство типа T.

Как получить свойство, когда я не знаю T? Я не могу использовать Action <>, если не знаю T.

РЕШЕНИЕ:

Внедрить не общий интерфейс;

public interface IGetGenericTypeInstance
{
    object GenericTypeInstance();
}

Теперь я могу передать объект IGetGenericTypeInstance, а GenericTypeInstance вернет свойство как объект типа.

C #, дженерики, литье,
Похожие вопросы