代码之家  ›  专栏  ›  技术社区  ›  John Rudy

在.NET 4中使用接口基类型的协方差?

  •  2
  • John Rudy  · 技术社区  · 14 年前

    我有一些使用Linq to SQL创建的实体。其中六个实体(主要表示下拉列表中的值)实现了我调用的接口 IValue . 我这样做是因为UI层必须考虑到几个特殊情况——特别是,如果记录的原始值被标记为已删除,那么应该显示什么。

    存储库有多种 ListAllXXX 这些人的方法。所有这些都将类型化的泛型列表返回到适当的实体类型。一个例子:

    public static List<ContactType> ListAllContactTypes(DeletedOptions getDeleted)
    { /* requisite code */ }
    

    ContactType 是否实施 价值观 当然。

    还有另一组服务设计用于实际检索特定于UI的列表。因此,基本模式是:

    // One of these for each entity type
    public static List<IValue> GetContactTypeList(ContactType target)
    {
        List<IValue> ret = LovRepository.ListAllContactTypes(DeletedOptions.NoDeleted);
        PrepList(ret, target);
    
        return ret;
    }
    
    // All of the above methods use this guy
    private static void PrepList(List<IValue> list, IValue targetEntity)
    {
        list.Insert(0, new DummyValue() { Description = "Add New ... ", ID = 0 });
    
        if (targetEntity != null && !(list.Contains(targetEntity))
            list.Add(new DummyValue() { Description = "[deleted]", ID = -1 });
    }
    

    (我可能应该注意到 DummyValue 是我创建的一个简单类,它也实现 价值观 ,其生活的全部目的是作为“添加新的”和“删除的”菜单选项。)

    所有这些都是因为我不想写几十行几乎相同的代码——这就是我认为我们有协方差的全部原因。

    这里写的代码不能编译。我试过手动铸造 List<IValue> ListAllContactTypes 行;编译,但在运行时由于无效的强制转换异常而失败。

    我怎么能到我想去的地方?使用接口的通用方差是否有限制?如果是这样,有没有一个简单的方法?如果不是,我是否会被降级为编写一组高度重复但略有不同的代码?(我是 真正地 试图避免。)

    这可能是一个副本,但我的google fu现在让我失望了。如果是,请投票相应关闭。(如果是这样的话,我会投决定性的一票!)

    1 回复  |  直到 14 年前
        1
  •  1
  •   driis    14 年前

    co-和contravariance只能用于委托和接口声明,因此代码无法工作。但是,您可以使用强制转换扩展方法强制转换结果,并使代码工作:

    List<IValue> ret = LovRepository.ListAllContactTypes(DeletedOptions.NoDeleted)
        .Cast<IValue>().ToList();
    

    这将强制转换列表中的每个元素,并创建一个新的ivalue元素列表。

    你需要这个的原因,与类型安全有关。当然,在您的示例代码中,没有问题。但方法ListAllContactTypes声明为返回类型列表<ContactType>。如果您可以将其分配给list<ivalue>,则可以将任何ivalue放入-因此,如果其他代码希望列表显式包含contacttype,则该代码将中断。考虑这个例子:

    List<ContactType> listOfContactType =  // Some method ....
    List<IValue> list = listOfContactType; // This line not allowed.
    

    如果进行了编译,您将能够执行以下操作:

    list.Insert(0,new DummyType());
    

    但你仍需考虑原始参考文献,在此基础上应允许:

    ContactType contact = listOfContactType[0];  // Woops, element is not ContactType.
    

    不能这样做,因为列表中的元素是dummyType。幸好编译器早早救了我们。