基类和派生类中的链接方法

Zdr*_*nev 5 c#

我有2个类,一个派生自另一个:

class Animal
{
    public Animal AnimalMethod() 
    {
        // do something 
        return this; 
    }   
}

class Dog : Animal
{
    public Dog DogMethod()
    {
         // do something
         return this;
    }
}

var dog = new Dog();
dog.DogMethod().AnimalMethod(); // 1 - this works   
dog.AnimalMethod().DogMethod(); // 2 - this doesn't
Run Code Online (Sandbox Code Playgroud)

如何更改我的声明以便能够按照上面的"2"顺序调用方法,以便获得更流畅的API?

Joh*_* Wu 9

使用通用扩展方法

流畅/链接方法最适合作为通用扩展方法.通用扩展方法知道实例变量的类型,并且可以将其作为传入的相同类型返回.

class Animal
{
    public string CommonProperty { get; set; }
}

class Dog : Animal
{
    public string DogOnlyProperty { get; set; }
}

static class ExtensionMethods
{
    static public T AnimalMethod<T>(this T o) where T : Animal
    {
        o.CommonProperty = "foo";
        return o;
    }
    static public T DogMethod<T>(this T o) where T : Dog
    {
        o.DogOnlyProperty = "bar";
        return o;
    }

}

class Example
{
    static public void Test()
    {
        var dog = new Dog();
        dog.DogMethod().AnimalMethod(); // 1 - this works   
        dog.AnimalMethod().DogMethod(); // 2 - this works now

        Console.WriteLine("CommonProperty = {0}", dog.CommonProperty);
        Console.WriteLine("DogOnlyProperty = {0}", dog.DogOnlyProperty);

        var animal = new Animal();
        animal.AnimalMethod();
        //animal.DogMethod();                //Does not compile
        //animal.AnimalMethod().DogMethod(); //Does not compile
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

CommonProperty = foo

DogOnlyProperty =吧

如果您需要私有/受保护的访问权限,则需要解决

扩展方法的一个缺点是它们无法访问私有或受保护的成员.你的实例方法可以.这对我来说并不是一个问题(对于整个LINQ库来说,它似乎也不是问题,它被编写为扩展方法).但是如果您需要访问权限,则有一种解决方法.

您需要两次实现"链接"方法 - 一次作为实例上的接口方法和一个简单的包装器(一行代码)作为简单调用第一个方法的扩展方法.我们在实例上使用接口方法,以便编译器不会尝试通过扩展方法选择实例方法.

interface IPrivateAnimal
{
    Animal AnimalMethod();
}

interface IPrivateDog
{
    Dog DogMethod();
}

class Animal : IPrivateAnimal
{
    protected virtual string CommonProperty { get; set; }  //notice this is nonpublic now

    Animal IPrivateAnimal.AnimalMethod()  //Won't show up in intellisense, as intended
    {
        this.CommonProperty = "plugh";
        return this;
    }
}

class Dog : Animal, IPrivateDog
{
    private string DogOnlyProperty { get; set; }  //notice this is nonpublic now

    Dog IPrivateDog.DogMethod()  //Won't show up in intellisense
    {
        this.DogOnlyProperty = "xyzzy";
        return this;
    }
}

static class ExtensionMethods
{
    static public T AnimalMethod<T>(this T o) where T : class, IPrivateAnimal
    {
        return o.AnimalMethod() as T;  //Just pass control to our hidden instance method
    }
    static public T DogMethod<T>(this T o) where T : class, IPrivateDog
    {
        return o.DogMethod() as T;  //Just pass control to the instance method
    }
}

class Example
{
    static public void Test()
    {
        var dog = new Dog();
        dog.DogMethod().AnimalMethod(); 
        dog.AnimalMethod().DogMethod(); 


        Console.WriteLine("CommonProperty = {0}", typeof(Dog).GetProperty("CommonProperty", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dog));
        Console.WriteLine("DogOnlyProperty = {0}", typeof(Dog).GetProperty("DogOnlyProperty", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dog));
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

CommonProperty = plugh

DogOnlyProperty = xyzzy


Bra*_*NET 6

你可以使用一个技巧来做到这一点; 虽然我不确定我会推荐它.make Animalgeneric和所有fluent方法都返回type参数:

class Animal<T> where T : Animal<T>
{
    public T AnimalMethod() { return (T)this; }
}
Run Code Online (Sandbox Code Playgroud)

现在你的狗继承了狗的动物形态

class Dog : Animal<Dog>
{
    public Dog DogMethod() { return this; }
}
Run Code Online (Sandbox Code Playgroud)

现在,由于初始方法将返回a Dog,您可以调用DogMethod它.这将是非常难以阅读; 但它会实现你的目标.

我在C#interactive中进行了测试,看起来很有效.

显然,这被称为"奇怪的重复"模式等等.https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

  • 嗯,"奇怪的反复出现"模式​​; 总是适合周五晚上! (5认同)