我试图制作一个流畅的界面,其中包含许多扩展基本描述符的泛型和描述符。 我把这个放在github仓库中,因为在这里粘贴所有的代码会使它不可读。扩展方法和类型推断
在阅读了Eric Lippert的关于类型限制的文章(http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx)和阅读No type inference with generic extension method之后,我对该主题有了更好的理解,但我仍然有疑问。
假设你有一些类,允许流畅电话:
var giraffe = new Giraffe();
new ZooKeeper<Giraffe>()
.Name("Jaap")
.FeedAnimal(giraffe);
var reptile = new Reptile();
new ExperiencedZooKeeper<Reptile>()
.Name("Martijn")
.FeedAnimal(reptile)
.CureAnimal(reptile);
的类是这样的:
public class ZooKeeper<T>
where T : Animal
{
internal string name;
internal List<T> animalsFed = new List<T>();
// this method needs to be fluent
public ZooKeeper<T> Name(string name)
{
this.name = name;
return this;
}
// this method needs to be fluent
public ZooKeeper<T> FeedAnimal(T animal)
{
animalsFed.Add(animal);
return this;
}
}
public class ExperiencedZooKeeper<T> : ZooKeeper<T>
where T : Animal
{
internal List<T> animalsCured = new List<T>();
// this method needs to be fluent
// but we must new it in order to be able to call CureAnimal after this
public new ExperiencedZooKeeper<T> Name(string name)
{
base.Name(name);
return this;
}
// this method needs to be fluent
// but we must new it in order to be able to call CureAnimal after this
public new ExperiencedZooKeeper<T> FeedAnimal(T animal)
{
base.FeedAnimal(animal);
return this;
}
// this method needs to be fluent
public ExperiencedZooKeeper<T> CureAnimal(T animal)
{
animalsCured.Add(animal);
return this;
}
}
我试图摆脱的“新”方法ExperiencedZooKeeper
隐藏实现ZooKeeper
。区别在于ExperiencedZooKeeper
中的new
方法返回正确的类型。没有new
方法AFAIK没有办法做到这一点。
我试图采取的另一种方法是将'setters'移动到扩展方法。这非常适用的,请将.Name()方法,但它引入了ZooKeeperBase
其中包含了内场:
public abstract class ZooKeeperBase
{
internal string name;
}
public class ZooKeeper<T> : ZooKeeperBase
where T : Animal
{
internal List<T> animalsFed = new List<T>();
// this method needs to be fluent
public ZooKeeper<T> FeedAnimal(T animal)
{
animalsFed.Add(animal);
return this;
}
}
public static class ZooKeeperExtensions
{
// this method needs to be fluent
public static TZooKeeper Name<TZooKeeper>(this TZooKeeper zooKeeper, string name)
where TZooKeeper : ZooKeeperBase
{
zooKeeper.name = name;
return zooKeeper;
}
}
但是这个确切的方法为FeedAnimal(T动物)不能正常工作,它需要一个额外的类型参数:
// this method needs to be fluent
public static TZooKeeper FeedAnimal<TZooKeeper, T>(this TZooKeeper zooKeeper, T animal)
where TZooKeeper : ZooKeeper<T>
where T : Animal
{
zooKeeper.animalsFed.Add(animal);
return zooKeeper;
}
这仍然是确定的和行之有效的,你仍然可以流利地叫它:
new ExperiencedZooKeeper<Reptile>()
.Name("Martijn")
.FeedAnimal(reptile)
.CureAnimal(reptile);
真正的问题开始当我试图让下面的方法流利的:
public static TZooKeeper Favorite<TZooKeeper, T>(this TZooKeeper zooKeeper, Func<T, bool> animalSelector)
where TZooKeeper : ZooKeeper<T>
where T : Animal
{
zooKeeper.favoriteAnimal = zooKeeper.animalsFed.FirstOrDefault(animalSelector);
return zooKeeper;
}
不能调用Favorite
这样的:
new ExperiencedZooKeeper<Reptile>()
.Name("Eric")
.FeedAnimal(reptile)
.FeedAnimal(new Reptile())
.Favorite(r => r == reptile)
,因为它会导致同样的问题No type inference with generic extension method,但是,这种情况下要稍微复杂一些,因为我们已经有一个类型参数描述我们需要的T的TZookKeeper。但是,像埃里克Lipperts博客文章,类型约束不是签名的组成部分:
The type arguments for method 'TestTypeInference5.ZooKeeperExtensions.Favorite<TZooKeeper,T>(TZooKeeper, System.Func<T,bool>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
有关完整代码,请参见https://github.com/q42jaap/TestTypeInference 在此回购自述实际上说明了我试图解决现实生活中的问题。
所以真正的问题是,是否有创建这种流利的方法风格,而不添加ZooKeeper的每个方法到ZooKeeper的每个子类的方法,new
隐藏ZooKeeper本身的方法?
我之前忘记了给Favorite的调用,看看Program5.cs中有两个不同的调用,一个不编译,一个绝对不流利! – Jaap 2013-05-08 18:11:05