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

C# 当类型从同一接口派生时合并可观察序列,c#,.net,system.reactive,C#,.net,System.reactive,我有一种情况,我想合并两个可观测序列。该序列由类似代码示例中的Ex1的消息组成。我不明白为什么它不能编译,当Ex2按照预期编译和运行时 谁能给我解释一下吗 class Message<T> where T : IFoo {} interface IFoo {} class Foo : IFoo {} class Bar : IFoo {} class Program { static void Ex1() { var o1 = Observable.

我有一种情况,我想合并两个可观测序列。该序列由类似代码示例中的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();
    }
}
类消息,其中T:IFoo{}
接口IFoo{}
类Foo:IFoo{}
类栏:IFoo{}
班级计划
{
静态void Ex1()
{
var o1=Observable.Interval(TimeSpan.fromMillicles(200))。选择(x=>newmessage());
var o2=可观察的.Interval(TimeSpan.frommilluses(200)).Select(x=>newmessage());
o1.合并(o2).订阅(控制台.写入线);
Console.ReadKey();
}
静态void Ex2()
{
var o1=Observable.Interval(TimeSpan.frommissions(200)).Select(x=>newfoo());
var o2=可观察的.Interval(TimeSpan.fromMilliconds(200)).Select(x=>newbar());
o1.合并(o2).订阅(控制台.写入线);
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>>>'
Error CS1929'IObservable'不包含'Merge'的定义,而最佳扩展方法重载'Observable.Merge(IObservable,int)'需要'IObservable'类型的接收器

非常感谢

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

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

那么第一种方法呢

第一个集合的类型为
Message
。此
消息的一般类型为
Foo
。请注意,这与
Message
不同,因为类型显式地是
Foo
,而不是它的超类或接口。类似地,
消息
的通用类型为
,而不是
IFoo

当编译器使用带有两个集合的
Merge
方法时(其中一个是
Message
Message
),它知道这两个
消息的泛型类型不同(一个
Foo
是一个
IFoo
Bar
是一个
IFoo
,但是一个
Foo
不是一个
Bar
,反之亦然)。因此编译器给出了一个错误,表示它需要一个相关类型的集合(
需要一个类型为“IObservable”的接收器)

还有最后一件事需要注意


消息
类的定义是
类消息,其中T:IFoo{}
。所有
where
子句的意思是
T
必须是
IFoo
,就是这样。这并不意味着
消息是
消息
。你可以从
Foo
转换为
IFoo
,这是第二种方法中发生的事情,但它们不一样。

仅仅因为
Foo
源自
IFoo
它并不意味着
Message
源自
Message
-,它不属于,因此您不能像以前那样强制转换它。这同样适用于
Bar
IBar

让我们看看原因

让我们使用
列表
,而不是按照
消息
进行思考。我现在有以下代码:

var foos = new List<Foo>();

foos.Add(new Foo());
foos.Add(new Foo());
毕竟,我对
ifoos
的引用属于
List
类型,
Bar
源于
IFoo
,因此这是完全有效的代码

但是
ifoos
只是对
List
的强制转换,强制转换对象不会改变其底层类型。
List
只能接受类型为
Foo
(或派生自
Foo
)的元素。现在,考虑到对编译器的神奇更改,我们已经创建了一种情况,即它可以接受
Bar

轰!运行时错误


这就是为什么cast
(列表)foos
失败的原因。这就是为什么cast
(消息)消息在您的问题中也失败的原因。

除了这里的其他答案之外:

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();
    }
}
接口IMessage,其中T:IFoo{}
类消息:IMessage,其中T:IFoo{}
接口IFoo{}
类Foo:IFoo{}
类栏:IFoo{}
班级计划
{
静态void Ex1()
{
var o1=可观察的.Interval(TimeSpan.frommilluses(200)).Select(x=>(IMessage)newmessage());
var o2=可观察的.Interval(TimeSpan.frommilluses(200))。选择(x=>(IMessage)new Message());
o1.合并(o2).订阅(控制台.写入线);
Console.ReadKey();
}
静态void Main(字符串[]参数)
{
Ex1();
}
}

仅仅因为
Foo
源于
IFoo
并不意味着
Message
源于
Message
-它不是-所以你不能像你那样施放它。同样的情况也适用于
Bar
IBar
。你问得很好。你给出了一个很好的例子,说明了你的想法工作正常,有什么不正常,还有一条清晰的错误消息。我印象深刻。你有更具体的情况需要帮助吗?非常感谢你的出色回答。这个例子让一切都非常清楚。我需要的是一个IMessage界面!感谢你花时间回复。我接受了Enigmativity的回答,因为他的excel四旬斋的例子。很高兴你能整理好。我必须仔细阅读协方差和逆方差
ifoos.Add(new Bar());
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();
    }
}