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.
现在的问题是在运行时从Dictionary中检索这个通用对象。或者更具体:将检索到的对象强制转换为特定的泛型类型。
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和开关。由于性能问题,我也不能使用某种反射。
12 个解决方案
#1
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界面。现在,实际的处理代码应成为应该实现此接口的不同消息类的一部分。
#2
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));
#3
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现在将是正确的类型,但由于你不知道在编译时什么类型的“键”,所以没有办法转换它并对它做任何有用的事情。
#4
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});
}
#5
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将该属性作为类型对象返回。
#6
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();
}
}
#7
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);
#8
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
}
...
#9
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作为参数,那么您可以找到它并动态调用它。
#10
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);
}
}
}
#11
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!无法将其作为缺乏声誉的评论发布。
#12
I struggled to solve a similar problem around data table classes instead of messages. The root issue mentioned above of casting a non-generic version of the class to a derived generic version was the same.
我努力解决围绕数据表类而不是消息的类似问题。上面提到的将类的非泛型版本转换为派生泛型版本的根本问题是相同的。
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. 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.
将从字典中强制转换的泛型类。
// 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 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>();
#1
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界面。现在,实际的处理代码应成为应该实现此接口的不同消息类的一部分。
#2
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));
#3
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现在将是正确的类型,但由于你不知道在编译时什么类型的“键”,所以没有办法转换它并对它做任何有用的事情。
#4
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});
}
#5
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将该属性作为类型对象返回。
#6
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();
}
}
#7
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);
#8
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
}
...
#9
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作为参数,那么您可以找到它并动态调用它。
#10
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);
}
}
}
#11
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!无法将其作为缺乏声誉的评论发布。
#12
I struggled to solve a similar problem around data table classes instead of messages. The root issue mentioned above of casting a non-generic version of the class to a derived generic version was the same.
我努力解决围绕数据表类而不是消息的类似问题。上面提到的将类的非泛型版本转换为派生泛型版本的根本问题是相同的。
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. 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.
将从字典中强制转换的泛型类。
// 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 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>();