2

我想“以某种方式”完成以下工作:

public class GenericsTest<T> where T: ISomeInterface
{
    private List<T> someList = new List<T>();

    public List<ISomeInterface> GetList()
    {
        return someList; //of course this doesn't even compile
    }
}

有没有办法做到这一点(如Java中的通配符),我只是错过了它?还是根本做不到?

编辑: 首先感谢大家的兴趣和您的回答/评论。其次,肯定有办法做到这一点,并且可能最简单和最有效的性能(虽然不确定)是创建一个正确类型的新列表并迭代地添加另一个列表的元素(在我们的例子中是 someList) . 问题在于所有这些新的差异事物,如果有办法以“泛型方式”来做到这一点,那么“入局”和“出局”。在 Java 中,这可能是:

public class GenericsTest<T extends SomeInterface> {
    private List<T> someList = new ArrayList<>();

    public List<? extends SomeInterface> getList() {
        return someList;
    }
}

所以我在徘徊是否? extends在 C# 中有等价物。我可以接受“不”作为简短的回答,我只是想我应该先问。

编辑 2: 一些用户认为,而另一些用户则坚持认为这是与铸造有关的问题的重复。由于我标记了最适合我的答案,为了清楚起见我解释一下。伙计们,我不想投。我只是在寻找具有extends约束的 Java 通配符的等效项。当然,Java 只有编译时泛型,这可能会有所不同。如果 C# 中的等效项通过强制转换完成,但我知道“在 Cat 列表中有一个 Dog 对象”的问题,所以我的问题是不同的。

4

5 回答 5

4

我相信这会成功:

public List<ISomeInterface> GetList()
{
    return someList.Cast<ISomeInterface>().ToList();
}

你也可以这样做:

public List<ISomeInterface> GetList()
{
    return someList.OfType<ISomeInterface>().ToList(); 
}

不同之处在于第一个选项将进行硬转换,这意味着如果其中一个元素没有实现ISomeInterface,则会抛出异常。第二个版本将只返回实现的实例,ISomeInterface而只会跳过没有实现的实例。

在您的示例中,您可以安全地使用该Cast选项,因为您可以保证所有实例都实现ISomeInterface.

于 2013-06-11T19:14:32.877 回答
3

您可以使用OfType<T>

public class GenericsTest<T> where T : ISomeInterface
{
    private List<T> someList = new List<T>();

    public List<ISomeInterface> GetList()
    {
        return someList.OfType<ISomeInterface>().ToList(); 
    }
}
于 2013-06-11T19:14:37.823 回答
2

在 C# 中,泛型变体的工作方式与在 Java 中不同。如果您想利用它,您需要使用变体的接口(或委托)。此类接口包括IEnumerable<T>and IReadOnlyList<T>(.Net 4.5 中的新接口),但不包括IList<T>,因为那不是类型安全的。

这意味着您可以执行以下操作:

public class GenericsTest<T> where T: ISomeInterface
{
    private List<T> someList = new List<T>();

    public IEnumerable<ISomeInterface> GetList()
    {
        return (IEnumerable<ISomeInterface>)someList;
    }
}
于 2013-06-11T23:21:07.280 回答
0

这就是问题所在。让我们假设这段代码可以编译:

class foo: ISomeInterface {}

class bar: ISomeInterface {}

GenericsTest<foo> testobject = GenericsTest<foo>();
List<ISomeinterface> alist = testobject.GetList();

内部testobject确实有一个列表,foo但是通过允许我们转换为 a,调用者无法知道如果我们尝试将 a插入列表List<ISomeinterface>中将会发生坏事。bar这就是不允许也不容易被允许的原因。

解决它的方法是:

  1. 在内部使用 a List<ISomeInterface>(如果集合变得异构是可以的)
  2. 如果只读列表正常,则创建具有所需类型的列表副本。如果您需要在某些情况下更改列表,您可以在GenericTest类上添加类型安全的方法来完成此操作。
于 2013-06-11T23:49:14.040 回答
0

Java 和 .NET 的一个主要弱点是,用于封装可变对象身份的可变类型引用与用于封装其状态的引用没有区别。特别是,如果代码调用getList然后尝试修改返回的列表,则不清楚这种尝试是否应该修改由 持有的列表GenericsTest<T>,或者是否应该简单地修改由该方法创建的列表的副本(离开仅原始列表)。也不清楚返回的列表是否getList应该受到未来对GenericsList<T>.

如果您的目标是允许调用者拥有一个新列表,该列表中填充了原始列表中的项目,但它可以根据需要对其进行操作,我建议您使用AsList方法或定义接口

interface ICopyableToNewList<out T> {
    // List<T> ToList(); // Can't be an actual interace member--must use extension method
    int Count {get;}
}

实现一个ToList()作用于该接口的扩展方法:

public static List<T> ToList<T>(this ICopyableToNewList<T> src)

并让你的班级实施ICopyableToNewList<T>T这样的声明将允许代码通过强制转换GenericTest<T>to来获取任何类型的新列表,该列表是 的超类型ICopyableToNewList<desiredType>

于 2013-06-11T23:13:45.547 回答