C#泛型类型不可赋值

C#泛型类型不可赋值,c#,generics,C#,Generics,我想用泛型编写一个CommandProcessor。其思想是通过单个对象(CommandProcessor本身)发出命令,然后该对象标识处理给定命令的命令处理程序 但是,以下代码无法编译,我无法理解原因: class GenericCommandProcessor : ICommandProcessor { private readonly IDictionary<Type, IList<ICommandHandler<ICommand>>> _han

我想用泛型编写一个CommandProcessor。其思想是通过单个对象(CommandProcessor本身)发出命令,然后该对象标识处理给定命令的命令处理程序

但是,以下代码无法编译,我无法理解原因:

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