当类型派生自同一接口时合并可观察序列



我有一个情况,我想合并两个可观察的序列。该序列由消息组成,如代码示例中的 Ex1 所示。我不明白为什么当 Ex2 按预期编译和运行时它不编译。

谁能向我解释一下?

class Message<T> where T : IFoo {}
interface IFoo {}
class Foo : IFoo {}
class Bar : IFoo {}
class Program
{
static void Ex1()
{
var o1 = Observable.Interval(TimeSpan.FromMilliseconds(200)).Select(x => new Message<Foo>());
var o2 = Observable.Interval(TimeSpan.FromMilliseconds(200)).Select(x => new Message<Bar>());
o1.Merge<Message<IFoo>>(o2).Subscribe(Console.WriteLine);
Console.ReadKey();
}
static void Ex2()
{
var o1 = Observable.Interval(TimeSpan.FromMilliseconds(200)).Select(x => new Foo());
var o2 = Observable.Interval(TimeSpan.FromMilliseconds(200)).Select(x => new Bar());
o1.Merge<IFoo>(o2).Subscribe(Console.WriteLine);
Console.ReadKey();
}
}

Visual Studio 2017 中的 .Net Framework 4.6.1 中的错误:

Error   CS1929  'IObservable<Message<Foo>>' does not contain a definition for 'Merge' and the best extension method overload 'Observable.Merge<Message<IFoo>>(IObservable<IObservable<Message<IFoo>>>, int)' requires a receiver of type 'IObservable<IObservable<Message<IFoo>>>'

提前非常感谢!

仅仅因为Foo是从IFoo派生的,并不意味着Message<Foo>派生自Message<IFoo>- 它不是 - 所以你不能像你一样投射。这同样适用于BarIBar.

让我们看看为什么。

与其从Message<T>的角度思考,不如用List<T>。我现在有这个代码:

var foos = new List<Foo>();
foos.Add(new Foo());
foos.Add(new Foo());

很好。

现在,就像您的问题一样,当您尝试将Message<Foo>更改为Message<IFoo>时,让我们尝试将其更改为List<IFoo>

List<IFoo> ifoos = (List<IFoo>)foos; // Does not compile

假设它确实编译了 - 为了解释为什么你的代码不起作用 - 所以我会有一个ifoos,我可以用它来做到这一点:

ifoos.Add(new Bar());

毕竟,我对ifoos的引用是List<IFoo>型的,并且Bar派生自IFoo,因此这将是完全有效的代码。

ifoos只是List<Foo>的强制转换,转换对象不会更改其基础类型。List<Foo>只能接受类型Foo(或派生自Foo(的元素。现在,鉴于我们对编译器的神奇更改,我们已经创建了一个可以接受的情况Bar.

繁荣!运行时错误。

这显然应该是演员(List<IFoo>)foos失败的原因。这就是为什么演员(Message<IFoo>)Message<Foo>在你的问题中也失败的原因。

我们将从第二种方法的工作原理开始。

第二种方法创建两个类型为FooBar的集合。但是在Merge方法中,要使用的类型明确是接口IFoo。这告诉该方法这是要存储在新集合中的对象类型:IEnumerable<IFoo>。该集合接受类型IFoo的项目,并且由于FooBar都从接口继承,因此集合接受它们。如果要从Merge方法中删除 type 参数,或者显式指定FooBar作为类型,则会遇到与第一种方法相同的错误。

那么第一种方法呢?

第一个集合的类型为Message<Foo>。此Message的泛型类型为Foo。注意这与Message<IFoo>不同,因为类型是显式Foo的,而不是它的超类或接口。类似地,Message<Bar>具有通用类型的Bar,而不是IFoo

当编译器来到具有两个集合(Message<Foo>Message<Bar>之一(的Merge方法时,它知道两个Messages的泛型类型并不相同(FooIFooBarIFoo,但Foo不是Bar, 反之亦然(。因此,编译器给出一个错误,指出它需要一个相关类型的集合(requires a receiver of type 'IObservable<IObservable<Message<IFoo>>>'(。

还有最后一件事需要注意。

Message类的定义是class Message<T> where T : IFoo {}。所有where子句的意思是T必须是IFoo,仅此而已。这并不意味着Message<Foo>Message<IFoo>.您可以从Foo转换为IFoo,这就是第二种方法中发生的情况,但它们并不相同。

除了这里的其他答案:

interface IMessage<out T> where T : IFoo { }
class Message<T> : IMessage<T> where T:IFoo { }
interface IFoo { }
class Foo : IFoo { }
class Bar : IFoo { }
class Program
{
static void Ex1()
{
var o1 = Observable.Interval(TimeSpan.FromMilliseconds(200)).Select(x => (IMessage<IFoo>)new Message<Foo>());
var o2 = Observable.Interval(TimeSpan.FromMilliseconds(200)).Select(x => (IMessage<IFoo>)new Message<Bar>());
o1.Merge(o2).Subscribe(Console.WriteLine);
Console.ReadKey();
}
static void Main(string[] args)
{
Ex1();
}
}

相关内容

  • 没有找到相关文章

最新更新