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

发布于 2024-12-10 16:20:01 字数 988 浏览 3 评论 0原文

我希望能够在 C# 基类中拥有一个方法,可在多个派生类的对象上调用,该方法返回对象本身,并且让 CLR 知道该对象的真正类型 - ie,适当的派生类型。 有人可以建议一种方法吗?当然,除了返回类型协方差之外,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();
    }
}

我只能想到两种方法来实现这一点,但我不知道就像它们中的任何一个一样:

  1. 将 Method() 的结果转换为预期类型(例如Derived1 x = (Derived) d1.Method();)。但强制转换是魔鬼的工具,此外,该方法的目的是返回 Derived1Derived2 或 ...,而不是 Base< /code>.

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

I'd like to be able to have a method in a C# base class, callable on objects of several derived classes, that returns the object itself, and have the CLR know what type the object really is - i.e., the appropriate derived type. Can someone suggest a way to do it? Other, of course, than return type covariance, which C# doesn't have.

Something like this, except that Method()'s return type should be the type of the derived class, not the base:

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();
    }
}

I can only think of two ways to make this work, and I don't like either of them:

  1. Cast the result of Method() to the expected type (e.g., Derived1 x = (Derived) d1.Method();). But casts are the tool of the Devil, and besides, the intent of the method is to return a Derived1 or Derived2 or ..., not a Base.

  2. Declare Method() as abstract in the base and implement it separately in each derived class. But that runs exactly counter to the idea of factoring out common methods. The Method() would be identical in each case except for its return type.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

旧伤还要旧人安 2024-12-17 16:20:01

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

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
    }
}

I believe you can use the dynamic keyword on 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
    }
}
深海蓝天 2024-12-17 16:20:01

好吧,我看错问题了。我以为OP确实想要重写该方法。显然不是,所以泛型是前进的方向:

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

public class Derived1 : Base<Derived1>
{
}

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

public Base()
{
    if (this as T == null)
    {
        // throw some exception
    }
}

它很丑陋,但它会起作用......并且丑陋仅限于基类。


原始答案

一种方法是将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;
    }
}

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

Okay, I misread the question. I'd thought the OP did want to override the method. Apparently not, so generics are the way forward:

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

public class Derived1 : Base<Derived1>
{
}

There's still a cast, but that's unfortunately unavoidable as far as I'm aware. You'd almost certainly want to check it in the constructor, too:

public Base()
{
    if (this as T == null)
    {
        // throw some exception
    }
}

It's ugly, but it'll work... and the ugliness is confined to the base class.


Original answer

One way to do it would be to put Method into a generic interface and implement it explicitly:

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;
    }
}

It's not nice, but it would work... where possible, I think I'd try to avoid needing it, to be honest. (And yes, I've come across similar situations when implementing protocol buffers. It's annoying.)

掩耳倾听 2024-12-17 16:20:01

似乎有很多方法可以实现这一目标:
也可以使用扩展方法。
这样做的好处是允许您处理不属于您的类,并且您只需执行一次,而不是为每个派生/基类执行一次。
这将完全满足您的需求。

public class BaseClass
{

}
public class DerivedClass: BaseClass
{

}
public static class BaseClassHelpers
{
    public static T Method<T>(this T b) where T : BaseClass
    {
        return b;
    }
}    

使用中:

DerivedClass d = new DerivedClass();
DerivedClass dd = d.Method();
Console.WriteLine(dd.GetType());

控制台结果:

DerivedClass

Seems there are many ways to accomplish this:
Also possible to do with Extension Methods.
Which has the benefit of allowing you to do with classes you don't own, and you only have to do it once, not once for each derived/base class.
This will do exactly what you are looking for.

public class BaseClass
{

}
public class DerivedClass: BaseClass
{

}
public static class BaseClassHelpers
{
    public static T Method<T>(this T b) where T : BaseClass
    {
        return b;
    }
}    

in use:

DerivedClass d = new DerivedClass();
DerivedClass dd = d.Method();
Console.WriteLine(dd.GetType());

result in console:

DerivedClass
缪败 2024-12-17 16:20:01

也有泛型但没有接口:

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;
}

Also with generics but without the interface:

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;
}
波浪屿的海角声 2024-12-17 16:20:01

如果您的方法具有类型 T 的参数,则不需要显式泛型参数。

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

public class Derived1 : Base { }
public class Derived2 : Base { }

Derived1 d1 = new Derived1();
Derived1 x = d1.Method<Derived1>();
Derived2 d2 = new Derived2();
Derived2 y = d2.Method<Derived2>();

If your method has parameters of type T, then the explicit generic arguments are not needed.

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

public class Derived1 : Base { }
public class Derived2 : Base { }

Derived1 d1 = new Derived1();
Derived1 x = d1.Method<Derived1>();
Derived2 d2 = new Derived2();
Derived2 y = d2.Method<Derived2>();
兰花执着 2024-12-17 16:20:01

这是一个无需强制转换的解决方案

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

    // It is worth to do this if you need the "this" of the derived class often in the base class
    protected abstract T ThisDerived { get; }
}

public class Derived1 : Base<Derived1>
{
    protected override Derived1 ThisDerived{ get { return this; } } 
}

public class Derived2 : Base<Derived2>
{
    protected override Derived2 ThisDerived { get { return this; } }
}

This is a solution without casting

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

    // It is worth to do this if you need the "this" of the derived class often in the base class
    protected abstract T ThisDerived { get; }
}

public class Derived1 : Base<Derived1>
{
    protected override Derived1 ThisDerived{ get { return this; } } 
}

public class Derived2 : Base<Derived2>
{
    protected override Derived2 ThisDerived { get { return this; } }
}
雪落纷纷 2024-12-17 16:20:01

有一种非常简单的方法可以使用 new 关键字和隐藏签名来实现所需的结果:

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

class Derived1: Base 
{ 
    public new Derived1 Method () { return this ; }
}

class Derived2: Base
{
    public new Derived2 Method () { return this ; }
}

There is a very simple way of achieving the desired result with the new keyword and hide-by-signature:

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

class Derived1: Base 
{ 
    public new Derived1 Method () { return this ; }
}

class Derived2: Base
{
    public new Derived2 Method () { return this ; }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文