繁体   English   中英

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

[英]chaining methods in base and derived class

我有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

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

使用通用扩展方法

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

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

输出:

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

输出:

CommonProperty = plugh

DogOnlyProperty = xyzzy

你可以使用一个技巧来做到这一点; 虽然我不确定我会推荐它。 使Animal通用,所有流畅的方法都返回类型参数:

class Animal<T> where T : Animal<T>
{
    public T AnimalMethod() { return (T)this; }
}

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

class Dog : Animal<Dog>
{
    public Dog DogMethod() { return this; }
}

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

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

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

唯一的简单方法是(在Dog ):

public new Dog AnimalMethod() 
{
    base.AnimalMethod();
    return this; 
}

这是“方法隐藏”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM