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

我有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(this T o) where T : Animal { o.CommonProperty = "foo"; return o; } static public T DogMethod(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(this T o) where T : class, IPrivateAnimal { return o.AnimalMethod() as T; //Just pass control to our hidden instance method } static public T DogMethod(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通用,所有流畅的方法都返回type参数:

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

现在你的狗inheritance了狗的动物形态

 class Dog : Animal { 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; } 

这是“方法隐藏”。