我正在尝试创建一个具有许多泛型和描述符的流畅接口,这些描述符扩展了基本描述符。我将其放在 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 后,我对该主题有了更好的理解,但仍有疑问。
假设您有一些允许流畅调用的类:
类的样子如下:
我试图消除
我尝试采取的另一种方法是将“setters”移到扩展方法中。这对于.Name()方法很有效,但它引入了一个包含内部字段的
但是这种精确的方法对于FeedAnimal(T animal)并不起作用,它需要一个额外的类型参数:
这仍然是可以的,而且运行良好,您仍然可以流畅地调用它:
您不能这样调用
在阅读了 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
中的“new”方法,以隐藏ZooKeeper
的实现。不同之处在于ExperiencedZooKeeper
中的“new”方法返回正确的类型。据我所知,没有办法在没有new
方法的情况下做到这一点。我尝试采取的另一种方法是将“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 animal)并不起作用,它需要一个额外的类型参数:
// 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)
因为这会导致与泛型扩展方法无类型推断相同的问题,但是这种情况稍微复杂一些,因为我们已经有了一个描述所需T的Type参数TZookKeeper。但是像Eric 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。 这个存储库中的README实际上解释了我试图解决的现实问题。
所以问题真正在于,是否有一种方法可以创建这种流畅的方法风格,而不必向ZooKeeper的每个子类添加每个ZooKeeper方法,并使用new
隐藏ZooKeeper本身的方法?