C#泛型类型不可赋值
我想用泛型编写一个CommandProcessor。其思想是通过单个对象(CommandProcessor本身)发出命令,然后该对象标识处理给定命令的命令处理程序 但是,以下代码无法编译,我无法理解原因:C#泛型类型不可赋值,c#,generics,C#,Generics,我想用泛型编写一个CommandProcessor。其思想是通过单个对象(CommandProcessor本身)发出命令,然后该对象标识处理给定命令的命令处理程序 但是,以下代码无法编译,我无法理解原因: class GenericCommandProcessor : ICommandProcessor { private readonly IDictionary<Type, IList<ICommandHandler<ICommand>>> _han
class GenericCommandProcessor : ICommandProcessor
{
private readonly IDictionary<Type, IList<ICommandHandler<ICommand>>> _handlers =
new Dictionary<Type, IList<ICommandHandler<ICommand>>>();
public void Register<TCommand>(ICommandHandler<TCommand> handler)
where TCommand : ICommand
{
IList<ICommandHandler<ICommand>> handlers = GetHandlers<TCommand>();
handlers.Add(handler); // <-- This doesn't compile
}
public void Process<TCommand>(TCommand command)
where TCommand : ICommand
{
IList<ICommandHandler<ICommand>> handlers = GetHandlers<TCommand>();
foreach (var commandHandler in handlers)
{
commandHandler.Handle(command);
}
}
private IList<ICommandHandler<ICommand>> GetHandlers<TCommand>()
{
Type commandType = typeof(TCommand);
IList<ICommandHandler<ICommand>> handlers;
if (!_handlers.TryGetValue(commandType, out handlers))
{
handlers = new List<ICommandHandler<ICommand>>();
_handlers.Add(commandType, handlers);
}
return handlers;
}
}
编译器返回以下错误:
cannot convert from 'GenericCommandHandlerTest.ICommandHandler<TCommand>' to 'GenericCommandHandlerTest.ICommandHandler<GenericCommandHandlerTest.ICommand>'
我通过从IoC(在我的例子中是Castle Windsor)解析命令处理程序列表而避免了这个问题,我赞成拥有注册处理程序列表的字典,但我很想理解为什么在CLR级别,这段代码无法编译。我想我只是看不见树木
非常感谢。只需将您的方法更改为:
public void AddListItem(IListItem listItem)
{
_items.Add(listItem);
}
这里不需要使用泛型
正如其他人已经说过的:即使没有更改,您的代码也可以编译,因此请更新您的示例代码
修复示例后更新:无法将类型为
ICommandHandler
的变量添加到IList
,因为ICommandHandler
和ICommandHandler
是两种不同的类型,尽管TCommand
实现了ICommand
。如果它能起作用,我的第一个答案也会是正确的,而且你不需要首先让你的方法变得通用
我想这会有帮助,但不幸的是,在这种情况下不支持它。Edit
就像丹尼尔说的,你要找的是协方差
如果您使用的是C#4,它实际上是存在的,但对您没有帮助。要使此示例起作用,ICommandHandler
需要是反向变量。如果TCommand在ICommandHandler
中只是一个out
参数,您可以将ICommandHandler
定义为
interface ICommandHandler<out TCommand> where TCommand: ICommand { ... }
而不是
public void AddListItem(IListItem listItem)
{
_items.Add(listItem);
}
我做了以下工作,并且能够编译
public interface IListItem
{
int MyProperty { get; set; }
}
public class ListProvider
{
private IList<IListItem> _items = new List<IListItem>();
public void AddListItem<T>(T listItem) where T : IListItem
{
_items.Add(listItem);
}
}
公共接口IListItem
{
int MyProperty{get;set;}
}
公共类列表提供程序
{
私有IList_items=新列表();
public void AddListItem(T listItem),其中T:IListItem
{
_添加(列表项);
}
}
对我来说也很好
class Program
{
private static readonly IList<IListItem> _items = new List<IListItem>();
public static void AddListItem<T>(T listItem) where T : IListItem
{
_items.Add(listItem);
foreach (var item in _items)
{
Console.WriteLine(item.ToString());
}
Console.ReadLine();
}
static void Main(string[] args)
{
Test tester = new Test();
AddListItem(tester);
}
}
internal interface IListItem
{
}
public class Test : IListItem
{
}
类程序
{
私有静态只读IList_items=new List();
公共静态void AddListItem(T listItem),其中T:IListItem
{
_添加(列表项);
foreach(var项目在_项目中)
{
Console.WriteLine(item.ToString());
}
Console.ReadLine();
}
静态void Main(字符串[]参数)
{
测试测试仪=新测试();
AddListItem(测试仪);
}
}
内部接口
{
}
公共类测试:IListItem
{
}
从根本上说,问题在于您试图将编译时安全性与无法表达的概念混为一谈:字典将typeof(t)
映射到与t
相关的内容。在类似的情况下,我发现围绕编译时安全性设计API很有用,但保留一些需要强制转换的代码。在本例中,只需要使用GetHandlers()
方法(顺便说一句,它不是线程安全的-我希望这不重要)
下面是完整的课程:
class GenericCommandProcessor : ICommandProcessor
{
private readonly IDictionary<Type, object> _handlers =
new Dictionary<Type, object>();
public void Register<TCommand>(ICommandHandler<TCommand> handler)
where TCommand : ICommand
{
IList<ICommandHandler<TCommand>> handlers = GetHandlers<TCommand>();
handlers.Add(handler);
}
public void Process<TCommand>(TCommand command)
where TCommand : ICommand
{
IList<ICommandHandler<TCommand>> handlers = GetHandlers<TCommand>();
foreach (var commandHandler in handlers)
{
commandHandler.Handle(command);
}
}
private IList<ICommandHandler<TCommand>> GetHandlers<TCommand>()
{
Type commandType = typeof(TCommand);
object untypedValue;
if (!_handlers.TryGetValue(commandType, out untypedValue))
{
untypedValue = new List<ICommandHandler<TCommand>>();
_handlers.Add(commandType, untypedValue);
}
return (IList<ICommandHandler<TCommand>>) untypedValue;
}
}
class GenericCommandProcessor:ICommandProcessor
{
专用只读IDictionary _处理程序=
新字典();
公共无效寄存器(ICommandHandler)
其中TCommand:ICommand
{
IList handlers=GetHandlers();
添加(handler);
}
公共作废进程(TCommand命令)
其中TCommand:ICommand
{
IList handlers=GetHandlers();
foreach(处理程序中的var commandHandler)
{
Handle(command);
}
}
私有IList GetHandlers()
{
Type commandType=typeof(TCommand);
对象非类型值;
if(!\u handlers.TryGetValue(commandType,out untypedValue))
{
untypedValue=新列表();
_Add(commandType,untypedValue);
}
返回(IList)非类型值;
}
}
您可能会将字典的TValue
类型参数更改为限制性更强的参数(例如IList
),但这并没有多大区别。为什么要将该方法设置为泛型?T只能是IListItem,那么为什么还要麻烦使用一个通用方法呢?将相同的方法粘贴到VS中,在ICollection
上仅替换IListItem
,因为我没有IListItem
,它编译成功。哦!对此很抱歉-我将代码直接写到SO.com,试图给出一个简单的示例。我现在发布了一个更完整的示例,以及确切的编译器错误。为了简洁起见,我省略了其他一些课程,但如果必要的话,我可以在这里提供。这都是杰夫的错。为什么不编译C代码呢?也许这是一个简化的例子。无论如何,它在我的机器上工作
alsoYes-这是一个过度简化的致命例子,我现在已经完全替换了它。抱歉浪费您的时间(我愚蠢地将原始示例直接写到SO.com,它还没有实现C#编译器:)@Neil:我为您添加了另一个答案。我不同意您的解释:“您需要知道每个TCommand都是ICommand,这显然不是真的”。嗯,很明显,每个TCommand都是一个ICommand,这是真的,因为这个限制。@Daniel:你说得对。我的意思是“你需要知道每个ICommand
都是TCommand
”。修正了。在我的真实例子中,我确实需要泛型。这是一个过度简化的致命例子,我现在已经完全替换了它。很抱歉浪费您的时间(我愚蠢地将原始示例直接写到SO.com,它还没有实现C#编译器:))感谢Jon-在我转向温莎之前,使用object
和casting是我可能的解决方案之一,所以很高兴看到我没有愚蠢。真正意义上的conf
public interface IListItem
{
int MyProperty { get; set; }
}
public class ListProvider
{
private IList<IListItem> _items = new List<IListItem>();
public void AddListItem<T>(T listItem) where T : IListItem
{
_items.Add(listItem);
}
}
class Program
{
private static readonly IList<IListItem> _items = new List<IListItem>();
public static void AddListItem<T>(T listItem) where T : IListItem
{
_items.Add(listItem);
foreach (var item in _items)
{
Console.WriteLine(item.ToString());
}
Console.ReadLine();
}
static void Main(string[] args)
{
Test tester = new Test();
AddListItem(tester);
}
}
internal interface IListItem
{
}
public class Test : IListItem
{
}
class GenericCommandProcessor : ICommandProcessor
{
private readonly IDictionary<Type, object> _handlers =
new Dictionary<Type, object>();
public void Register<TCommand>(ICommandHandler<TCommand> handler)
where TCommand : ICommand
{
IList<ICommandHandler<TCommand>> handlers = GetHandlers<TCommand>();
handlers.Add(handler);
}
public void Process<TCommand>(TCommand command)
where TCommand : ICommand
{
IList<ICommandHandler<TCommand>> handlers = GetHandlers<TCommand>();
foreach (var commandHandler in handlers)
{
commandHandler.Handle(command);
}
}
private IList<ICommandHandler<TCommand>> GetHandlers<TCommand>()
{
Type commandType = typeof(TCommand);
object untypedValue;
if (!_handlers.TryGetValue(commandType, out untypedValue))
{
untypedValue = new List<ICommandHandler<TCommand>>();
_handlers.Add(commandType, untypedValue);
}
return (IList<ICommandHandler<TCommand>>) untypedValue;
}
}