通过IList在通用列表中设置后门

8

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

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<object>。


当你试图添加一个错误类型的对象时,会抛出异常,但这是否意味着泛型列表也会进行类型检查呢?当然会。泛型是强类型的。为什么泛型在加载之前不进行类型检查是不必要的呢? - Gerald Davis
我可以问一下为什么抽象化会使泛型无法使用吗?泛型和抽象化应该相互支持。在必要的情况下,您可以使用泛型类型的占位符,例如List<T>。 - chiccodoro
在某些场景下,如果您有很多工厂、接口等使用自己的类的通用版本,事情会变得非常困难或不可能,因为您事先不知道您的T,而且还需要许多其他类是通用的。我认为Marc也可以证明将泛型注入到您的类结构中可能存在的潜在问题 :) - Homde
4个回答

3

List<T> 显式实现了 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可能会更高效。 - Homde
@MattiasK:我非常清楚这点。但是,它编译不过。你只有在使用where T: class约束的情况下才能在泛型参数上使用as关键字。列表可以包含结构体。 - SLaks

1

在大多数情况下,使用 IList 是可以的;而且比使用反射或 dynamic 实现相同的功能肯定更快。

是的,它会添加类型检查(通过强制转换/取消装箱),但这不会很繁琐。如果 T 是一个 struct,那么你也有一些装箱/取消装箱,但这也不像人们担心的那样糟糕。

在那种情况下,IList 对我来说就可以了。


0

你的解决方案看起来很不错,没有任何问题。

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

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

    return Count - 1;
}

它不进行类型检查;它只是一个try/catch语句块。


(T)item 隐式地是一种类型检查。 - Marc Gravell
是和否。当然它检查类型,但这里没有检查。更像是让我们看看能走多远。如果catch把它转换成类似于-1(结果而不是异常),这可能会让我哭泣。通过“检查”,我的意思是涉及一个if的内容。 - Pieter van Ginkel

0

我喜欢第二种方法。

您可以传递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))
   }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接