我有两个类,一个派生自另一个:
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?
答案 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;。