C# 在.NET中使用隐式转换替代多重继承

C# 在.NET中使用隐式转换替代多重继承,c#,.net,api-design,implicit-conversion,C#,.net,Api Design,Implicit Conversion,在这种情况下,我希望某种类型的对象能够作为两种不同的类型使用。如果其中一个“基本”类型是接口,这不会是一个问题,但在我的情况下,它们最好都是具体类型 我正在考虑将其中一个基类型的方法和属性的副本添加到派生类型,并添加从派生类型到该基类型的隐式转换。然后,用户将能够通过直接使用复制的方法、将其分配给基类型的变量或将其传递给采用基类型的方法,将派生类型视为基类型 看起来这个解决方案很适合我的需要,但我是否遗漏了什么?是否存在这样的情况:在使用API时,这将不起作用,或者可能会增加混乱而不是简单性 编

在这种情况下,我希望某种类型的对象能够作为两种不同的类型使用。如果其中一个“基本”类型是接口,这不会是一个问题,但在我的情况下,它们最好都是具体类型

我正在考虑将其中一个基类型的方法和属性的副本添加到派生类型,并添加从派生类型到该基类型的隐式转换。然后,用户将能够通过直接使用复制的方法、将其分配给基类型的变量或将其传递给采用基类型的方法,将派生类型视为基类型

看起来这个解决方案很适合我的需要,但我是否遗漏了什么?是否存在这样的情况:在使用API时,这将不起作用,或者可能会增加混乱而不是简单性

编辑:有关我的特定场景的更多详细信息:

这是为了将来重新设计指标的编写方式,这是一个自动化交易系统开发环境。价格数据表示为一系列条形图,这些条形图具有给定时段(1分钟、1天等)的开盘价、低位价、高位价和收盘价的值。指示器对一系列数据进行计算。简单指标的一个例子是移动平均指标,它给出其输入的最近n个值的移动平均值,其中n是用户指定的。移动平均线可以应用于条形收盘,也可以应用于另一个指示器的输出以使其平滑

每次输入新条时,指示器都会计算该条输出的新值

大多数指示器只有一个输出系列,但有时有多个输出比较方便(请参阅),我想支持这一点

因此,指标需要从“组件”类派生,该类具有在新数据进入时调用的方法。然而,对于只有一个输出序列的指标(这是它们中的大多数),最好将它们本身作为一个序列。这样,用户就可以对SMA的当前值使用
SMA.Current
,而不必使用
SMA.Output.Current
。同样,
Indicator2.Input=Indicator1优于
Indicator2.Input=Indicator1.Output。这似乎没有什么不同,但我们的很多目标客户都不是专业的.NET开发人员,所以我想让这一切尽可能简单

我的想法是对只有一个输出序列的指标进行从指标到其输出序列的隐式转换

然后,用户将能够通过直接使用复制的方法、将其分配给基类型的变量或将其传递给采用基类型的方法,将派生类型视为基类型

然而,这将有不同的表现。在继承的情况下,您只是传递对象。但是,通过实现隐式转换器,在转换发生时,您将始终构建一个新的对象。这可能是非常意外的,因为在这两种情况下,它的行为会完全不同

就我个人而言,我认为这是一个返回新类型的方法,因为它会让最终用户清楚地看到实际的实现。

我看到两个问题:

  • 用户定义的类型转换运算符通常不易发现——它们不会在IntelliSense中显示

  • 对于隐式用户定义的类型转换运算符,应用该运算符时通常不明显

事实并非如此,您根本不应该定义类型转换运算符,但在设计解决方案时必须记住这一点

一个易于发现、易于识别的解决方案是定义显式转换方法:

class Person { }

abstract class Student : Person
{
    public abstract decimal Wage { get; }
}

abstract class Musician : Person
{
    public abstract decimal Wage { get; }
}

class StudentMusician : Person
{
    public decimal MusicianWage { get { return 10; } }

    public decimal StudentWage { get { return 8; } }

    public Musician AsMusician() { return new MusicianFacade(this); }

    public Student AsStudent() { return new StudentFacade(this); }
}
用法:

void PayMusician(Musician musician) { GiveMoney(musician, musician.Wage); }

void PayStudent(Student student) { GiveMoney(student, student.Wage); }

StudentMusician alice;
PayStudent(alice.AsStudent());

听起来你的方法不支持交叉转换。真正的多重继承是不可能的

一个C++的例子,它具有多重继承:

class A {};
class B {};
class C : public A, public B {};

C o;
B* pB = &o;
A* pA = dynamic_cast<A*>(pB); // with true MI, this succeeds
A类{};
B类{};
C类:公共A、公共B{};
C o;
B*pB=&o;
A*pA=动态(pB);//有了真正的MI,这就成功了

您没有提供太多的详细信息,因此这里尝试从您提供的内容中进行回答

看看基本的区别:
当您有一个基本类型
B
和一个派生类型
D
时,如下所示的赋值:

B my_B_object = my_D_object;
将引用指定给同一对象。另一方面,当
B
D
是独立的类型,它们之间存在隐式转换时,上述赋值将创建
my_D_对象的副本
,并将其存储在
my_B_对象
上(如果
B
是一个类,则为对其的引用)

总之,“真实”继承是按引用工作的(对引用的更改会影响多个引用共享的对象),而自定义类型转换通常是按值工作的(这取决于您如何实现它,但为转换器实现接近“按引用”的行为几乎是疯狂的):每个引用将指向其自己的对象

你说你不想使用接口,但为什么?使用组合接口+助手类+扩展方法(需要C#3.0和.Net 3.5或更高版本)可以非常接近真正的多重继承。看看这个:

interface MyType { ... }
static class MyTypeHelper {
    public static void MyMethod(this MyType value) {...}
}
对每种“基本”类型这样做将允许您为您想要的方法提供默认实现

这些方法不会像开箱即用的虚拟方法那样工作;但你可以通过反思来实现这一点;您需要在Helper类的实现中执行以下操作:

  • 使用
    value.GetType()检索
    System.Type
  • 查找该类型是否有与签名匹配的方法
  • 如果找到了匹配的方法,请调用它并重试
    if(System.Reflection.Assembly.GetCallingAssembly()!=System.Reflection.Assembly.GetExecutingAssembly())
        throw new Exception("Don't call me. Don't call me!. DON'T CALL ME!!!");
    
    class Bar
    {
        double Open { get; }
        double Low { get; }
        double High { get; }
        double Close { get; }
    }
    
    class Series : IObservable<Bar>
    {
        // ...
    }
    
    static class IndicatorExtensions
    {
        public static IObservable<double> MovingAverage(
            this IObservable<Bar> source,
            int count)
        {
            // ...
        }
    }
    
    Series series = GetSeries();
    
    series.MovingAverage(20).Subscribe(average =>
    {
        txtCurrentAverage.Text = average.ToString();
    });