c#面向对象返回类型this;呼叫孩子

本文关键字:呼叫 孩子 this 面向对象 返回类型 | 更新日期: 2023-09-27 18:12:49

我有两个类,每个类在所有函数中返回自己:

public class Parent{
   public Parent SetId(string id){
      ...
      return this
   }
}
public class Child : Parent{
   public Child SetName(string id){
      ...
      return this
   }
}

我想启用这种API:

new Child().SetId("id").SetName("name");

不能访问SetName,因为SetId返回Parent, SetNameChild上。

如何?

c#面向对象返回类型this;呼叫孩子

如果您真的想要这种流畅的行为,并且Parent类可以抽象,那么您可以这样实现:

public abstract class Parent<T> where T : Parent<T>
{
    public T SetId(string id) {
        return (T)this;
    }
}
public class Child : Parent<Child>
{
    public Child SetName(string id) {
        return this;
    }
}

现在可以这样写:

new Child().SetId("id").SetName("name");

我可能在这里错过了重点,但看起来你真的只是试图设置属性。如果您将它们作为属性公开,则可以使用对象初始化器调用。

var child = new Child()
{
    Id = value1;
    Name = value2;
}

你也可以调用一个方法,而不是在初始化式中设置一个值。

我觉得很奇怪,您想从当前实例中返回对类的当前实例的引用。为了访问对象的属性和方法,您是否已经有了这样一个引用?你可能在考虑克隆吗?

除非我错过了关于你的模型的一些东西,似乎一个适当的初始化方案,一些无聊的属性定义,也许一个工厂类可能更像你正在寻找的。似乎您还可以考虑将带有ID的Parent设置为不可变属性,尽管您的需求可能会为这样做创建一个真正的理由。

下面是一些可能有用的类定义。现在圣路易斯还早,我还没有完全喝醉,所以我可能错过了什么…: -)

父:

public class Parent
{
    protected string _id;
    // Protected default constructor so tht this class can be inherited:
    protected Parent() { }
    // REquired to set ID at initialization; 
    public Parent(String ID)
    {
        _id = ID;
    }
    // Read-only:
    public String ID
    {
        get { return _id; }
    }

    // This seems a little dodgy. Why would you want to return a reference
    // to the current instance as a result of resetting the ID?
    public Parent SetID(String ID)
    {
        _id = ID;
        return this;
    }
}

父工厂:

public class ParentFactory
{
    public static Parent NewParent(String ID)
    {
        Parent newParent = new Parent(ID);
        return newParent;
    }
}

孩子:

public class Child : Parent
{
    private string _name;
    // Protected default constructor so tht this class can be inherited:
    protected Child() { }
    // Initialize a new Child with the ID property required for the parent:
    public Child(String ID)
    {
        base.SetID(ID);
    }

    // Initialize a new child with both properties set:
    public Child(String ID, String Name) : this(ID)
    {
        _name = Name;
    }
    // This could be read-only as well:
    public String Name
    {
        get { return _name; }
        set { _name = value; }
    }

    // Again, seems kinda dodgy: 
    public Child SetName(String name)
    {
        return this;
    }
}

A Child Factory:

public class ChildFactory
{
    public static Child  NewChild(String ID)
    {
        Child newChild = new Child(ID);
        return newChild;
    }

    public static Child  NewChild(String ID, String Name)
    {
        Child newChild = new Child(ID, Name);
        return newChild;
    }
}

Davide Piras是对的,一种方法是使用虚方法,或者更好的是使用接口。

查看此代码

public interface INode
{
    INode SetName(string aName);
    INode SetId(string aId);
}
public class Parent : INode
{
    public virtual INode SetId(string id)
    {
        ...
        return this
    }
    public virtual INode SetName(string aId)
    {
        return this;
    }
}
public class Child : Parent
{
   public override INode SetName(string id)
   {
      ...
      return this;
   }
}

看起来您正在尝试创建一个流畅的界面。您确定这将是开发人员使用这些类的最简单方法吗?

如何使用简单的旧属性'Name'和'Id'?这很好且易于使用,特别是在使用集合初始化器语法时。

我在这里发现了一个类似的问题,但是对于java语言:如何使java父类方法返回子类的对象

试图编译它,但不工作,看到我的剪辑和看到我的评论,基本上看起来像在Java中,我们可以将其转换为T,但在c#现在。有趣的…

(这不是你问题的解决方案,我只是报告我试图做什么,我能走多远,如果有人知道一些魔法让这个工作保持这样的设计…)欢迎:))

class Program
{
    static void Main(string[] args)
    {
        var obj = new Child().SetId("id").SetName("aaa");
    }
}
public class Parent<T>
{
    public virtual T SetId(string id)
    {
        // this does not work
        return (T)this;
        // this compiles but is NOT what we want :(
        //return default(T);
    }
}
public class Child : Parent<Child>
{
    public Child SetName(string name)
    {
        return this;
    }
}

这是怎么回事:((孩子)(新的子().SetId (" id ")) .SetName("名字");

这里的解释是,虽然对象的运行时类型是Child,但是c#在编译你想要支持的表达式时使用的静态类型推断是SetId("id")的静态类型是Parent。因为SetId是在Parent类中定义的。

实际上你的对象是Child类型的,并且有一个SetName方法。因此,SetName对你是可用的,但只有当你告诉c#把对象当作子对象。在你的表达式中你没有,但是因为Parent没有SetName方法,你的代码不会进行类型检查,所以它不会编译。

如果你做了转换,c#会检查类型以防万一,但它会做你想要的调用。