C# C语言中的铸造问题

C# C语言中的铸造问题,c#,casting,covariance,contravariance,C#,Casting,Covariance,Contravariance,我有一个从IList继承的接口: public interface IBase {} public class Derived : IBase {} public interface IMyList : IList<IBase> {} 我想将IMyList类型的变量强制转换为IList或List类型,无论哪种类型更容易或更有意义。最好的方法是什么 请注意,我使用的是.NET 3.5。您不能在.NET 3.5中执行此操作,但可以在.NET 4.0中执行此操作。您不能在.NET 3.5

我有一个从IList继承的接口:

public interface IBase {}
public class Derived : IBase {}
public interface IMyList : IList<IBase> {}
我想将IMyList类型的变量强制转换为IList或List类型,无论哪种类型更容易或更有意义。最好的方法是什么


请注意,我使用的是.NET 3.5。您不能在.NET 3.5中执行此操作,但可以在.NET 4.0中执行此操作。

您不能在.NET 3.5中执行此操作,但可以在.NET 4.0中执行。

直接转换将不适用,因为在列表中可能会出现许多问题,例如,可能包含派生类型以外的类型

只要您不修改列表,在这种情况下,IEnumerable就可以工作,您可以简单地:

var validItems = myList.Cast<Derived>();
然后对结果进行迭代

编辑

根据OP的以下评论,还有两件事需要提及:

1如果您需要IList,只需添加到上面并调用myList.Cast.ToList

2如果这些是真正的需求,那么您的代码就没有任何意义。如果IMyList应该只包含派生对象,那么IMyList应该从IList派生。请记住,虽然您知道只有一种类型实现IBase接口,但编译器并不是那么具体


为了使用接口而到处使用接口对任何人都没有帮助

直接强制转换并不合适,因为在IMyList中可能会出现许多问题,例如可能包含派生类型以外的类型等

只要您不修改列表,在这种情况下,IEnumerable就可以工作,您可以简单地:

var validItems = myList.Cast<Derived>();
然后对结果进行迭代

编辑

根据OP的以下评论,还有两件事需要提及:

1如果您需要IList,只需添加到上面并调用myList.Cast.ToList

2如果这些是真正的需求,那么您的代码就没有任何意义。如果IMyList应该只包含派生对象,那么IMyList应该从IList派生。请记住,虽然您知道只有一种类型实现IBase接口,但编译器并不是那么具体

为了使用接口而到处使用接口对任何人都没有帮助

由于IList允许T类型的读写操作,因此该接口既不是共变的,也不是逆变的。因此,你想做的事是做不到的。想象一下下面的代码:

var myList = new MyListImlementation();
myList.Add(new BaseImplementation());
var castList = (IList<Derived>)myList; // this is what you want

// this would break, because myList contains elements of type BaseImplementation.
Derived d = castList[0];               
由于IList允许T类型的读写操作,因此该接口既不是协变量,也不是反变量。因此,你想做的事是做不到的。想象一下下面的代码:

var myList = new MyListImlementation();
myList.Add(new BaseImplementation());
var castList = (IList<Derived>)myList; // this is what you want

// this would break, because myList contains elements of type BaseImplementation.
Derived d = castList[0];               

.Net 3.5没有通用协方差。证明:

[TestFixture]
class Class1
{
    [Test]
    public void test()
    {
        var list = new List<SuperClass>();
        list.Add(new SuperClass());

        var castedList = ((List<BaseClass>)list);
    }
}

public class BaseClass
{
    public string a { get; set; }
}

public class SuperClass : BaseClass
{
    public string b { get; set; }
}
将无法成功编译

贾斯汀·尼斯纳(Justin Niessner)对此的回应是,这项工作

var validItems = myList.Cast<Derived>();  
被张贴。这将起作用,但将导致整个列表的枚举,尽管此枚举是延迟的,并且还返回可枚举的。要转换列表并最终生成IList,可以使用以下命令

[Test]
public void CanConvertListToBaseClass()
{
    var list = new List<SuperClass>();
    list.Add(new SuperClass());

    var castedList = list.Cast<BaseClass>().ToList();
    Assert.That(castedList, Is.InstanceOf<IList<BaseClass>>());
}

然而,这是一种相当野蛮的方法。这将产生一个新的独立的IList,并将强制枚举整个列表。

.Net 3.5没有通用协方差。证明:

[TestFixture]
class Class1
{
    [Test]
    public void test()
    {
        var list = new List<SuperClass>();
        list.Add(new SuperClass());

        var castedList = ((List<BaseClass>)list);
    }
}

public class BaseClass
{
    public string a { get; set; }
}

public class SuperClass : BaseClass
{
    public string b { get; set; }
}
将无法成功编译

贾斯汀·尼斯纳(Justin Niessner)对此的回应是,这项工作

var validItems = myList.Cast<Derived>();  
被张贴。这将起作用,但将导致整个列表的枚举,尽管此枚举是延迟的,并且还返回可枚举的。要转换列表并最终生成IList,可以使用以下命令

[Test]
public void CanConvertListToBaseClass()
{
    var list = new List<SuperClass>();
    list.Add(new SuperClass());

    var castedList = list.Cast<BaseClass>().ToList();
    Assert.That(castedList, Is.InstanceOf<IList<BaseClass>>());
}
然而,这是一种相当野蛮的方法。这将产生一个新的独立的IList,并将强制枚举整个列表

我正在将转换后的结果传递到一个接受IList的函数中

那么你就处在一个痛苦的世界里,这是你自己设计的。我的建议是首先找到其他方法来解决循环依赖性问题。正如您所发现的,将所有内容都转换成只有一个可能实现的接口是解决该问题的一种痛苦的方法。我不推荐

如果你不能做到这一点,那么我会尝试修复有问题的函数,这样它要么需要一个IList,要么需要一个IEnumerable,要么需要一个IEnumerable。优选一种IEnumerable溶液;事实上,大多数采用列表的方法只需要序列。如果你能解释一下为什么你需要在这里列出一个清单,这将有助于找到一个解决办法

如果你能让它花费一个IList或IEnumerable,那么你就完成了;您已经有了可以隐式转换为所需类型的东西

如果您可以使用IEnumerable,那么如果您确实知道它们都是派生的,则可以使用myListOfIBase.Cast;如果您怀疑其中一些可能不是派生类型,并且希望跳过它们并获取有效使用基础列表的IEnumerable,则可以使用myListOfIBase.OfType

如果无法更改有问题的函数,则可以创建自己的类,该类可以有效地使用基础列表:

sealed class MyProxyList : IList<Derived>
{
    IList<IBase> underlyingList;
    public MyProxyList(IList<IBase> underlyingList)
    {
        this.underlyingList = underlyingList;
    }
    ... now implement every member of IList<Derived> as 
    ... a call to underlyingList with a cast where necessary 
}
然后将新的MyProxyList传递给该方法

我正在将浇铸结果传递到f 需要一个IList的函数

那么你就处在一个痛苦的世界里,这是你自己设计的。我的建议是首先找到其他方法来解决循环依赖性问题。正如您所发现的,将所有内容都转换成只有一个可能实现的接口是解决该问题的一种痛苦的方法。我不推荐

如果你不能做到这一点,那么我会尝试修复有问题的函数,这样它要么需要一个IList,要么需要一个IEnumerable,要么需要一个IEnumerable。优选一种IEnumerable溶液;事实上,大多数采用列表的方法只需要序列。如果你能解释一下为什么你需要在这里列出一个清单,这将有助于找到一个解决办法

如果你能让它花费一个IList或IEnumerable,那么你就完成了;您已经有了可以隐式转换为所需类型的东西

如果您可以使用IEnumerable,那么如果您确实知道它们都是派生的,则可以使用myListOfIBase.Cast;如果您怀疑其中一些可能不是派生类型,并且希望跳过它们并获取有效使用基础列表的IEnumerable,则可以使用myListOfIBase.OfType

如果无法更改有问题的函数,则可以创建自己的类,该类可以有效地使用基础列表:

sealed class MyProxyList : IList<Derived>
{
    IList<IBase> underlyingList;
    public MyProxyList(IList<IBase> underlyingList)
    {
        this.underlyingList = underlyingList;
    }
    ... now implement every member of IList<Derived> as 
    ... a call to underlyingList with a cast where necessary 
}

然后将一个新的MyProxyList传递给该方法。

如果您完全确定所有实例都是派生类型的,那么您可以使用Cast,并注意前面提到的性能问题。如果列表中有可能出现派生以外的内容,那么您将希望使用OfType。非派生项将被排除,而不是抛出。

如果您完全确定所有实例都将是派生类型的,那么您可以使用Cast,前面已经提到了性能问题。如果列表中有可能出现派生以外的内容,那么您将希望使用OfType。非派生项将被排除,而不是抛出。

甚至在4.0中也不能。IList不支持它。-1:这个答案根本没有帮助,甚至没有回答我原来的问题。你甚至不能在4.0中使用。IList不支持。-1:这个答案一点帮助都没有,甚至没有回答我原来的问题。这行不通。最终结果必须至少是一个IList,因为我正在将转换后的结果传递给一个接受IList的函数。我还可以保证IMyList列表中除了派生之外不会有任何其他内容,因为它是从中继承的唯一类IBase@RobertDailey-如果这些是需求,那么您的代码就没有意义。如果IMyList应该只包含派生对象,它应该从IList而不是IList继承。@Robert:你不能指望编译器或运行时知道这一点。编译器合理地假设您使用接口是为了它们的预期目的,即让两个或多个不相关的类实现相同的功能。一个接口列表可以合理地预期包含多个不相关类型的对象,因此永远不能被视为一个特定类型的列表。@Michael:这种方法的问题是,1如果列表很大,代价会很高;2如果被调用方正在对列表进行编辑,如果被调用方没有这样做,那他们为什么要求IList而不是IEnumerable那么如何将编辑内容复制回原始列表?另一种方法是创建一个代理对象,将对原始列表的访问修复为正确的元素类型。@Robert:那么,您不应该使用任何涉及制作列表副本的技术,比如先执行强制转换,然后再执行ToList。您可能需要制作一个代理,将强制转换放在每个接口方法调用的顶部。这是行不通的。最终结果必须至少是一个IList,因为我正在将转换后的结果传递给一个接受IList的函数。我还可以保证IMyList列表中除了派生之外不会有任何其他内容,因为它是从中继承的唯一类IBase@RobertDailey-如果这些是需求,那么您的代码就没有意义。如果IMyList应该只包含派生对象,它应该从IList而不是IList继承。@Robert:你不能指望编译器或运行时知道这一点。编译器合理地假设您使用接口是为了它们的预期目的,即让两个或多个不相关的类实现相同的功能。一个接口列表可以合理地预期包含多个不相关类型的对象,因此永远不能被视为一个特定类型的列表。@Michael:这种方法的问题是,1如果列表很大,代价会很高;2如果被调用方正在对列表进行编辑,如果被调用方没有这样做,那他们为什么要求IList而不是IEnumerable那么如何将编辑内容复制回原始列表?另一种方法是创建一个pro
xy对象,该对象将对原始列表的访问修复为正确的元素类型。@Robert:那么,您不应该使用任何涉及制作列表副本的技术,比如先进行强制转换,然后再进行ToList。您可能需要制作一个代理,将强制转换放在每个接口方法调用的顶部。我花了相当多的时间才发布这篇文章,以了解.NET3.5中的协方差/逆变问题。这就是为什么我发布这个问题,因为我不确定这个问题的正确解决方案。不过谢谢你的提醒!罗伯特·戴利:我知道这种安瑟尔很常见,看起来很讨厌,但你可能想重新考虑一下你的方法。我认为没有合适的解决办法。我可以想出一些非常糟糕的解决办法,但没有什么看起来像样的。不幸的是,这是一个我无法修复的设计,这就是为什么我试图以这种方式解决问题。这是公司代码,已经建立,所以很遗憾,更改设计不是一个选项。我花了很多时间才发布这篇文章,以了解.NET3.5中的协方差/逆变问题。这就是为什么我发布这个问题,因为我不确定这个问题的正确解决方案。不过谢谢你的提醒!罗伯特·戴利:我知道这种安瑟尔很常见,看起来很讨厌,但你可能想重新考虑一下你的方法。我认为没有合适的解决办法。我可以想出一些非常糟糕的解决办法,但没有什么看起来像样的。不幸的是,这是一个我无法修复的设计,这就是为什么我试图以这种方式解决问题。这是公司代码,已经建立,因此很遗憾,更改设计不是一个选项。我最初没有说明这一点,但派生类是唯一继承/实现IBase的类,因此这不是问题。我的当前层次结构不可能导致运行时错误。@Robert:如果派生类是唯一实现IBase的类,那么为什么首先需要IBase?只需将程序中的每个IBase实例替换为派生的、已完成的、已解决的问题即可。@Eric请参阅我对评分最高的答案的评论。它解释了这背后的原因。我最初没有说明这一点,但派生类是唯一继承/实现IBase的类,所以这不是问题。我的当前层次结构不可能导致运行时错误。@Robert:如果派生类是唯一实现IBase的类,那么为什么首先需要IBase?只需将程序中的每个IBase实例替换为派生的、已完成的、已解决的问题即可。@Eric请参阅我对评分最高的答案的评论。它解释了这背后的原因。正如其他人所提到的,转换从来都不是合法的,而且有充分的理由。为了帮助您找到一个解决方法,了解IList在使用时将要执行的操作将非常有帮助。例如,当您获得它时,它将是只读的,还是您将尝试向其中添加项目,或者是什么?如果您只是在阅读,您是在使用随机访问,还是总是使用foreach从头到尾迭代整个过程?您是否对其使用LINQ查询?等等。正如其他人所提到的,这种转换从来都不是合法的,而且有充分的理由。为了帮助您找到一个解决方法,了解IList在使用时将要执行的操作将非常有帮助。例如,当您获得它时,它将是只读的,还是您将尝试向其中添加项目,或者是什么?如果您只是在阅读,您是在使用随机访问,还是总是使用foreach从头到尾迭代整个过程?您是否对其使用LINQ查询?等等