[英]Inherited Generic Type Unification
对于这样的场景:
public interface IAnimal
{
}
public interface IGiraffe : IAnimal
{
}
public interface IQuestionableCollection : IEnumerable<IAnimal>
{
void SomeAction();
}
public interface IQuestionableCollection<out T> : IQuestionableCollection, IEnumerable<T>
where T : IAnimal
{
}
public class QuestionableCollection<T> : IQuestionableCollection<T>
where T:IAnimal
{
// Implementation...
}
编译器将生成错误:
'IQuestionableCollection<T>' cannot implement both 'System.Collections.Generic.IEnumerable<IAnimal>' and 'System.Collections.Generic.IEnumerable<T>' because they may unify for some type parameter substitutions
这是有道理的,C#无法解决的两个接口之间确实存在歧义,除非它使用类型约束,而不是像@ericlippert 在这里解释的语言规范那样。
我的问题是我应该如何在这里实现同样的效果呢?
似乎我应该能够表达该集合对于基本接口是可枚举的。 (我想提供一组可以在不知道具体类型的情况下使用的方法,以及它使一些API /反射代码更清晰,所以我想将基本集合保持为非通用的,如果有的话可能。否则,就不需要两个接口。)
我能想到的唯一可编程实现类似于:
public interface IQuestionableCollectionBase
{
void SomeAction();
}
public interface IQuestionableCollection : IQuestionableCollectionBase, IEnumerable<IAnimal>
{
}
public interface IQuestionableCollection<out T> : IQuestionableCollectionBase, IEnumerable<T>
where T : IAnimal
{
}
public class QuestionableCollectionBase<T> : IQuestionableCollection
where T : IAnimal
{
protected List<T> _items = new List<T>();
public void SomeAction() { }
IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_items).GetEnumerator(); }
IEnumerator<IAnimal> IEnumerable<IAnimal>.GetEnumerator() { return ((IEnumerable<IAnimal>)_items).GetEnumerator(); }
}
public class QuestionableCollection<T> : QuestionableCollectionBase<T>, IQuestionableCollection<T>
where T : IAnimal
{
public IEnumerator<T> GetEnumerator() { return ((IEnumerable<T>)_items).GetEnumerator(); }
}
请注意,我必须将我想要在两个接口上使用的任何方法移动到基本方法,并为类本身提供两个级别的实现 - 这似乎我在这里跳过了足够的箍,我已经有了失踪的东西......
该如何实施?
最简单的解决方法是将IEnumerables从“is-a”更改为“has-a”,如下所示:
public interface IAnimal { }
public interface IGiraffe : IAnimal { }
public interface IQuestionableCollection
{
IEnumerable<IAnimal> Animals { get; }
void SomeAction();
}
public interface IQuestionableCollection<out T> : IQuestionableCollection
where T : IAnimal
{
new IEnumerable<T> Animals { get; }
}
public class QuestionableCollection<T> : IQuestionableCollection<T>
where T : IAnimal, new()
{
private readonly List<T> list = new List<T>();
public IEnumerable<T> Animals
{
get { return list; }
}
IEnumerable<IAnimal> IQuestionableCollection.Animals
{
get { return (IEnumerable<IAnimal>)list; }
}
public void SomeAction()
{
list.Add(new T());
}
}
class Giraffe : IGiraffe { }
[TestMethod]
public void test()
{
var c = new QuestionableCollection<Giraffe>();
IQuestionableCollection<Giraffe> i = c;
IQuestionableCollection<IGiraffe> i2 = i;
Assert.AreEqual(0, c.Animals.Count());
Assert.AreEqual(0, i.Animals.Count());
c.SomeAction();
i.SomeAction();
Assert.AreEqual(2, c.Animals.Count());
Assert.AreEqual(2, i.Animals.Count());
}
请注意,如果添加where T : class
约束,则可以避免在QuestionableCollection<T>
强制转换。
将IQuestionableCollection更改为非通用IEnumerable会对编译器问题进行排序。
public interface IQuestionableCollection : IEnumerable {...}
我已经看到MS在他们的集合中使用这种模式,使用IEnumerable
的非泛型版本和使用IEnumerable<T>
的泛型版本。
或者,使其他IEnumerable<IAnimal>
也会停止编译器错误,但这意味着在枚举时会返回IAnimals而不是T。
你可以试试这个:
public interface IAnimal
{
}
public interface IGiraffe : IAnimal
{
}
public interface IQuestionableCollection<T> : IEnumerable<T> where T : IAnimal
{
void SomeAction();
}
public interface IQuestionableCollection : IQuestionableCollection<IAnimal>
{
}
public class QuestionableCollection<T> : IQuestionableCollection<T>, IEnumerable<T>
where T : IAnimal
{
public void SomeAction() { }
public IEnumerator<T> GetEnumerator()
{
throw new NotImplementedException();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
鉴于语言的限制,您将无法解决IQuestionableCollection和IQuestionableCollection都实现通用接口的问题。
实质上,您指定的是IQuestionableCollection实现两个可能的列表,List和另一个List。 这里没有等级关系,因此无法解决。
话虽这么说,你必须用IQuestionableCollection上的IEnumerable替换IEnumerable,以便为编译器提供一个继承链。 实际上,除非你计划实现一个vanilla QuestionableCollection,否则实际上甚至没有必要声明基础IQuestionableCollection。
在我更新的代码中,我把它带到了QuestionableCollection的消费者,以说明QuestionableCollection()的枚举保留了IGiraffe的输入。
public interface IAnimal {}
public interface IGiraffe : IAnimal { }
public interface IQuestionableCollection : IEnumerable
{
void SomeAction();
}
public interface IQuestionableCollection<out T> : IQuestionableCollection, IEnumerable<T>
where T : IAnimal
{ }
public class QuestionableCollection<T> : IQuestionableCollection<T>
where T : IAnimal
{
private List<T> list = new List<T>();
public IEnumerator<T> GetEnumerator()
{
return list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void SomeAction()
{
throw new NotImplementedException();
}
}
class Program
{
static void Main(string[] args)
{
var questionable = new QuestionableCollection<IGiraffe>();
foreach (IGiraffe giraffe in questionable)
{
}
}
}
对于非通用实现
1你总是可以安全地向上翻
var questionable = new QuestionableCollection();
IEnumerabl<IAnimal> animals = questionable.OfType<IAnimal>();
-要么-
2您可以让NonGeneric QuestionableCollection类实现IEnumerable
public class QuestionableCollection : IQuestionableCollection, IEnumerable<IAnimal>
{
public IEnumerator<IAnimal> GetEnumerator()
{
var l = new List<Giraffe>();
l.Add(new Giraffe());
l.Add(new Giraffe());
return l.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void SomeAction()
{
throw new NotImplementedException();
}
}
然后,您可以在没有强制转换操作的情况下枚举。
var questionable = new QuestionableCollection();
foreach (IAnimal giraffe in questionable)
{
var i = giraffe;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.