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

时间:2017-08-04 22:25:12

标签: c#

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

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?

3 个答案:

答案 0 :(得分:9)

使用通用扩展方法

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

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 = bar

如果您需要私人/受保护的访问权限,则需要采取解决方法

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

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

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

答案 1 :(得分:6)

你可以使用一个技巧来做到这一点;虽然我不确定我会推荐它。使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

答案 2 :(得分:2)

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

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

这是&#34;方法隐藏&#34;。