【问题标题】:Backdooring Generic Lists through IList通过 IList 后门通用列表
【发布时间】:2010-11-12 13:02:06
【问题描述】:

我有一个场景,一个类加载一种类型的对象,由于抽象我不能使用泛型类(泛型往往像癌症一样传播:) 但我经常想使用检索到的对象的泛型版本,这导致了这样的代码(简化):

List<SomeClass> items = Storage.LoadItems(filename).OfType<SomeClass>().ToList();

其中LoadItems返回一个List,然后我意识到,为什么不改为有

public void LoadItems(string filename,IList list);

现在我可以这样做了

List<SomeClass> items = new  List<SomeClass>();
LoadItems(filename,items);

这应该更有效。它似乎也更灵活一些,因为我可以采用现有列表并添加新项目。所以我的问题是,这是一种常见的模式还是你有不同/更好的方法来实现这一点?

我也有点好奇你能做到这一点,如果你尝试添加一个错误类型的对象你会得到一个异常,但这是否意味着泛型列表也会进行类型检查? (好像有点没必要)

编辑 将模式修改为

实际上可能会更优雅一些
public IList LoadItems(string filename,IList list=null);

这样你可以流畅地使用语句,如果没有传递列表,你可以简单地实例化一个 List

【问题讨论】:

  • “如果你尝试添加一个错误类型的对象,你会得到一个异常,但这是否意味着泛型列表也会进行类型检查?”他们当然会。泛型是强类型的。为什么泛型在加载之前没有必要检查类型?
  • 请问为什么抽象应该使泛型不可用?泛型和抽象应该相互支持。您可以在必要时为泛型类型使用占位符,例如 List
  • 在某些情况下,如果您有很多工厂、接口等,使用(您自己的)类的通用版本会使事情变得非常困难或不可能,因为您预先不知道您的 T,而且它还需要很多其他类是通用的。我认为 Marc 也可以证明将泛型注入类结构的潜在问题 :)

标签: c# generics ilist


【解决方案1】:

List&lt;T&gt; 显式实现IList。 实现转换为T 并调用常规(通用)方法。

因此,只有在显式调用 IList 方法时才会进行类型检查。 例如:

void System.Collections.IList.Insert(int index, Object item) 
{
    ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(item, ExceptionArgument.item); 

    try {
        Insert(index, (T) item); 
    }
    catch (InvalidCastException) {
        ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T));
    } 
}

它不使用as 关键字,因为T 可能是一个值类型。
如果你写了where T : class,你只能写as T

【讨论】:

  • 啊,这是有道理的,尽管在您的示例中使用“as”和 if 语句而不是装箱和 try-catch 可能会更高效
  • @MattiasK:我很清楚这一点。但是,它不会编译。如果使用where T : class 约束泛型参数,则只能在泛型参数上使用as 关键字。列表可以包含结构。
【解决方案2】:

在大多数情况下使用IList 没问题;并且肯定比使用反射或dynamic 更快。

是的,它会添加一个类型检查(通过强制转换/取消装箱),但这不会很麻烦。如果Tstruct,那么您也有一些装箱/拆箱,但这也没有人们担心的那么糟糕。

在那种情况下,IList 对我来说没问题。

【讨论】:

    【解决方案3】:

    您的解决方案看起来确实不错,没有任何问题。

    关于Add,它并没有真正进行类型检查。你指的Add的代码是这样的:

    int System.Collections.IList.Add(Object item)
    {
        try {
            Add((T) item);
        }
        catch (InvalidCastException) {
            throw ...;
        }
    
        return Count - 1;
    }
    

    它不做类型检查;这只是一个尝试/捕获。

    【讨论】:

    • (T)item 隐含地是一个类型检查of sorts
    • 是和不是。当然它会检查类型,但这里没有检查。这更像是让我们看看我们能走多远。如果 catch 将它转换为类似 -1 的东西(结果而不是异常),这可能会让我哭泣。 “检查”是指涉及if :)。
    【解决方案4】:

    我喜欢第二种方法。

    您可以传递 IList,然后检查类型以查看它是否是泛型列表,如果是,则获取泛型类型并仅加载该类型的记录:

       Type itemType = typeof (object);
       if(list.GetType().GetGenericArguments().Length>0)
       {
           itemType = list.GetType().GetGenericArguments()[0];
       }
    
       for (int i = 0; i < recordCount; i++)
       {
          if(record.GetType().IsInstanceOfType(itemType))
       }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-17
      • 1970-01-01
      • 2015-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-12
      相关资源
      最近更新 更多