C# 中的强制转换问题

发布于 2024-11-19 07:28:56 字数 330 浏览 6 评论 0原文

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

public interface IBase {}
public class Derived : IBase {}
public interface IMyList : IList<IBase> {}

我想将 IMyList 类型的变量转换为 IListList 类型,以更容易或最有意义的为准。最好的方法是什么?

请注意,我使用的是 .NET 3.5

I have a interface that inherits from IList:

public interface IBase {}
public class Derived : IBase {}
public interface IMyList : IList<IBase> {}

I want to cast a variable of type IMyList to type IList<Derived> or List<Derived>, whichever is easier or makes the most sense. What's the best way to do this?

Note that I'm using .NET 3.5

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

稀香 2024-11-26 07:28:56

直接转换并不合适,因为可能会出现许多问题(IMyList 可能包含 Derived 以外的类型等),

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

var validItems = myList.Cast<Derived>();

然后迭代结果。

编辑

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

1)如果您需要一个IList,您可以简单地添加到上面并调用myList.Cast().ToList();

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

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

A direct cast isn't going to be suitable since there are numerous problems that could occur (IMyList may contain types other than Derived, etc.)

As long as you're not modifying the list (in which case, IEnumerable<Derived> would work) you could simply:

var validItems = myList.Cast<Derived>();

And then iterate over the result.

Edit

Based on the OP's comment below, there are two other things to mention:

1) If you need an IList<Derived>, you could simply add to the above and call myList.Cast<Derived>().ToList();

2) If those are truly the requirements, then your code doesn't make any sense. If IMyList should only ever contain Derived objects then IMyList should derive from IList<Derived>. Remember that while you know that there is only one type implementing the IBase interface, the compiler doesn't so be specific.

Using interfaces everywhere just for the sake of using interfaces doesn't help anybody!

ぃ双果 2024-11-26 07:28:56

我将转换结果传递到一个采用 IList 的函数中。

然后你就陷入了你自己设计的痛苦世界。我的建议是首先找到其他方法来解决循环依赖问题。正如您所发现的,将所有内容都放入只有一种可能实现的接口中是解决该问题的一种痛苦方法。我不推荐它。

如果您做不到这一点,那么我会尝试修复有问题的函数,以便它采用 IListIEnumerable>IEnumerable<派生>。优选IEnumerable解决方案之一;大多数采用列表的方法实际上只需要序列。如果您可以在此处解释为什么需要列表,这将有助于找到解决方法。

如果您可以使其采用 IListIEnumerable 那么您就完成了;您手中已经有一些可以隐式转换为所需类型的东西。

如果您可以使其采用 IEnumerable 那么您可以说 myListOfIBase.Cast() (如果您确实知道所有这些都是 Derived)或myListOfIBase.OfType() (如果您怀疑其中某些可能不是 Derived 类型并想跳过它们)并获取有效使用底层列表的 IEnumerable

如果您无法更改有问题的函数,那么您可以创建自己的类来有效地使用底层列表:

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 传递给该方法。

I am passing the casted result into a function that takes an IList<Derived>.

Then you are in a world of pain of your own devising. My advice would be first to find some other way to solve your circular dependency problem. Making everything into interfaces that have only one possible implementation is a painful way to solve that problem, as you have discovered. I don't recommend it.

If you can't do that then I would try to fix the offending function so that it either takes an IList<IBase>, an IEnumerable<IBase> or an IEnumerable<Derived>. Preferably one of the IEnumerable solutions; most methods that take lists in fact only need sequences. If you could explain why you need a list here, that would be helpful in trying to find a workaround.

If you can make it take an IList<IBase> or IEnumerable<IBase> then you're done; you already have something in hand that is implicitly convertible to the desired type.

If you can make it take an IEnumerable<Derived> then you can say myListOfIBase.Cast<Derived>() (if you really know that all of them are Derived) or myListOfIBase.OfType<Derived>() (if you suspect that some of them might not be of type Derived and want to skip them) and get an IEnumerable<Derived> that efficiently uses the underlying list.

If you cannot change the offending function then you can make your own class that efficiently uses the underlying list:

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 
}

And then pass a new MyProxyList to the method.

眼泪都笑了 2024-11-26 07:28:56

.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; }
}

不会编译成功。

解决方法

var validItems = myList.Cast<Derived>();  

Justin Niessner 的回复中发布了 。这将起作用,但会导致整个列表的枚举(尽管此枚举被推迟)并且还返回一个可枚举值。要转换列表并最终得到 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 does not have generic covariance. To demonstrate:

[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; }
}

Will not compile successfully.

In Justin Niessner's response, the work around

var validItems = myList.Cast<Derived>();  

was posted. This will work but will result in an enumeration of the entire list (although this enumeration is deferred) and also return an enumerable. To convert the list and end up with an IList you can use the following

[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>>());
}

This is a pretty brute-force approach however. This will result in a new and separate IList and will force an enumeration of the entire list.

格子衫的從容 2024-11-26 07:28:56

由于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];               

Since IList<T> allows read and write operations of type T, the interface is neither co- nor contravariant. Thus, what you want cannot be done. Imagine the following code:

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];               
机场等船 2024-11-26 07:28:56

如果您可以绝对确定所有实例都将是 Derived 类型,那么您可以使用 Cast<>(),并解决前面提到的性能问题。如果列表中可能存在除 Derived 之外的内容,那么您将需要使用 OfType() 来代替。非派生项目将被排除而不是抛出。

If you can be absolutely certain going forward that all instances will be of type Derived, then you can use Cast<>(), with the performance issues noted earlier. If there is a chance that something besides Derived will ever be in the list, then you'll want to use OfType<Derived>() instead. Non-Derived items would be excluded instead of throwing.

£噩梦荏苒 2024-11-26 07:28:56

在 .Net 3.5 中无法执行此操作,但在 .Net 4.0 中可以。

You can't do this in .Net 3.5, but you can in .Net 4.0.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文