即使在派生类中,基类方法也可以返回吗?

Ros*_*son 9 c# derived-class

我希望能够在C#基类中有一个方法,可以在几个派生类的对象上调用,返回对象本身,让CLR知道对象的实际类型 - 适当的派生类型. 有人可以建议一种方法吗? 其他当然,还有C#没有的返回类型协方差.

像这样的东西,除了Method()返回类型应该是派生类的类型,而不是基类:

public abstract class Base { 
    public Base Method() { return this; }
}

public class Derived1: Base { ... }

public class Derived2: Base { ... }

public class Main {
    public static int Main() {
        Derived1 d1 = new Derived1();
        Derived1 x = d1.Method();
        Derived2 d2 = new Derived2();
        Derived2 y = d2.Method();
    }
}
Run Code Online (Sandbox Code Playgroud)

我只能想到两种方法来完成这项工作,我不喜欢其中任何一种:

  1. 将Method()的结果转换为期望的类型(例如,Derived1 x = (Derived) d1.Method();).但是施法者是魔鬼的工具,此外,该方法的目的是返回一个Derived1Derived2或......,而不是一个Base.

  2. Method()在基类中声明为抽象,并在每个派生类中单独实现它.但这与分解常用方法的想法完全相反.的Method()将是在每种情况下,除了它的返回类型相同.

Jon*_*eet 11

好的,我误解了这个问题.我以为OP 确实想要覆盖这个方法.显然不是,所以泛型是前进的方向:

public abstract class Base<T> where T : Base<T>
{
    public T Method()
    {
        return (T) (object) this;
    }
}

public class Derived1 : Base<Derived1>
{
}
Run Code Online (Sandbox Code Playgroud)

还有一个演员阵容,但据我所知,这是不可避免的.你几乎肯定也想在构造函数中检查它:

public Base()
{
    if (this as T == null)
    {
        // throw some exception
    }
}
Run Code Online (Sandbox Code Playgroud)

它很难看,但它会起作用......丑陋只限于基类.


原始答案

一种方法是放入Method通用接口并明确实现它:

public interface IFoo<T> {
    T Method();
}

public abstract class Base : IFoo<Base>
{
    Base IFoo<Base>.Method()
    {
        return this;
    }
}

public class Derived1 : IFoo<Derived1>
{
    public Derived1 Method()
    {
        // If you need to call the base version, you'll
        // need ((IFoo<Base>)this).Method()
        return this;
    }
}
Run Code Online (Sandbox Code Playgroud)

这不好,但它会起作用......在可能的情况下,我想我会尽量避免需要它,说实话.(是的,我在实现协议缓冲时遇到过类似的情况.这很烦人.)


smo*_*oak 11

我相信你可以dynamic在C#4.0上使用关键字:

public abstract class Base { 
    public dynamic Method() { return this; }
}

public class Derived1: Base { ... }

public class Derived2: Base { ... }

public class Main {
    public static int Main() {
        Derived1 d1 = new Derived1();
        Derived1 x = d1.Method();
        Console.WriteLine(x.GetType()); // outputs Derived1
        Derived2 d2 = new Derived2();
        Derived2 y = d2.Method();
        Console.WriteLine(y.GetType()); // outputs Derived2
    }
}
Run Code Online (Sandbox Code Playgroud)

  • "动态"是魔鬼比恶魔更糟糕的工具.在这种特殊情况下,编译器和运行时合作为您插入一个演员. (3认同)
  • @ChrisMarisic"_为什么你会想要这个的最明显的例子是基本类使用的流利链接返回this_"这正是我当时想要做的. (3认同)

Mr.*_*dor 8

似乎有很多方法可以实现这一点:也可以使用扩展方法.
这样做的好处是允许你使用你不拥有的类,而你只需要执行一次,而不是每次派生/基类.这将完全符合您的要求.

public class BaseClass
{

}
public class DerivedClass: BaseClass
{

}
public static class BaseClassHelpers
{
    public static T Method<T>(this T b) where T : BaseClass
    {
        return b;
    }
}    
Run Code Online (Sandbox Code Playgroud)

正在使用:

DerivedClass d = new DerivedClass();
DerivedClass dd = d.Method();
Console.WriteLine(dd.GetType());
Run Code Online (Sandbox Code Playgroud)

导致控制台:

DerivedClass
Run Code Online (Sandbox Code Playgroud)

  • 这是我首选的解决方案,因为它是"最安全的" - 没有动态关键字而且没有强制转换,所以它永远不会在运行时失败. (2认同)

nem*_*esv 5

还有泛型但没有界面:

public abstract class Base<T> where T : Base<T>
{
    public virtual T Method()
    {
        return (T) this;
    }
}

public class Derived1 : Base<Derived1>
{
    public override Derived1 Method()
    {
        return base.Method();
    }
}

public class Derived2: Base<Derived2> { }

public class Program {
public static int Main() {
    Derived1 d1 = new Derived1();
    Derived1 x = d1.Method();
    Derived2 d2 = new Derived2();
    Derived2 y = d2.Method();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)