我有一个情况,我想合并两个可观察的序列。该序列由消息组成,如代码示例中的 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>
- 它不是 - 所以你不能像你一样投射它。这同样适用于Bar
和IBar
.
让我们看看为什么。
与其从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>
在你的问题中也失败的原因。
我们将从第二种方法的工作原理开始。
第二种方法创建两个类型为Foo
和Bar
的集合。但是在Merge
方法中,要使用的类型明确是接口IFoo
。这告诉该方法这是要存储在新集合中的对象类型:IEnumerable<IFoo>
。该集合接受类型IFoo
的项目,并且由于Foo
和Bar
都从接口继承,因此集合接受它们。如果要从Merge
方法中删除 type 参数,或者显式指定Foo
或Bar
作为类型,则会遇到与第一种方法相同的错误。
那么第一种方法呢?
第一个集合的类型为Message<Foo>
。此Message
的泛型类型为Foo
。注意这与Message<IFoo>
不同,因为类型是显式Foo
的,而不是它的超类或接口。类似地,Message<Bar>
具有通用类型的Bar
,而不是IFoo
。
当编译器来到具有两个集合(Message<Foo>
和Message<Bar>
之一(的Merge
方法时,它知道两个Messages
的泛型类型并不相同(Foo
是IFoo
,Bar
是IFoo
,但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();
}
}