向上转换为接口当类实现具有类型约束 T=Foo 的泛型 Interface 时
本文关键字:Foo 约束 类型 Interface 泛型 实现 转换 接口 | 更新日期: 2023-09-27 18:30:40
我有一个声明几个方法的接口(在 C# 中):
internal interface ILetter<T> where T:IEntity
{
List<T> GetRecords();
DocumentParameters GetDocumentParameters(T record);
void MarkRecordAsHandled(T record, bool letterSent);
}
我还有一个实现上述接口的抽象类,还添加了一堆通用功能。它看起来像这样:
abstract class AbstractLetter<T> : ILetter<T> where T:IEntity
{
public abstract List<T> GetRecords();
public abstract DocumentParameters GetDocumentParameters(T record);
public abstract void MarkRecordAsHandled(T record, bool letterSent);
protected readonly FamisContext FamisContext = new FamisContext();
protected bool KnownEmail(string socialSecurityNumber){ doing stuff }
}
最后,我从上面的基类(AbstractLetter)继承了不同的类。当然,它们包括在接口中声明的方法的特定实现。
我想做一个批处理作业,根据界面中的方法创建不同类型的字母。我想我可以通过制作一个列表来做到这一点,然后在每个元素上使用相同的方法循环访问列表。但显然我不能这样做 - 至少在没有显式类型转换的情况下不能这样做。
所以,我的问题是:我可以做类似下面的事情,但没有类型转换(当然还有更多的字母)吗?
var test = new List<ILetter<IEntity>> {new JobTrainingLetter() as ILetter<IEntity>};
好吧,初学者,new JobTrainingLetter() as ILetter<IEntity>
并不真正有效,除非您明确地将T
标记为"out",即协变。
如果将类型参数标记为out
,还应确保只能从"输出"位置访问T
。
将您的信件视为ILetter<IEntity>
并没有真正的意义,因为这意味着您可以使用以下方法:
DocumentParameters GetDocumentParameters(IEntity record);
在 ILetter<YourConcreteEntity>
的实例上。如果你尝试GetDocumentParameters(anotherConcreteEntity)
会发生什么?运行时异常。
我建议你多考虑一下你的设计。
我不完全确定我是否理解您的问题,但这就是您所追求的吗?
http://ideone.com/bE08rw
在 LinqPad 中:
interface IEntity
{
string Name { get; set; }
}
class FooEntity : IEntity
{
public string Name { get; set; }
public FooEntity()
{
Name = "foo";
}
}
interface ILetter<T> where T:IEntity
{
string EntityMetadata { get; set; }
List<T> GetRecords();
}
abstract class AbstractLetter<T> : ILetter<T> where T:IEntity
{
public abstract string EntityMetadata { get; set; }
public abstract List<T> GetRecords();
}
class JobLetter : AbstractLetter<FooEntity>
{
public override string EntityMetadata { get; set; }
public override List<FooEntity> GetRecords() { return null; }
public JobLetter()
{
var entity = new FooEntity();
EntityMetadata = entity.Name;
}
}
List<T> CreateLetterList<T, K>() where T : AbstractLetter<K>, new() where K : IEntity
{
return new List<T> { new T(), };
}
void Main()
{
var jobLetterList = CreateLetterList<JobLetter, FooEntity>();
foreach (var letter in jobLetterList)
{
Console.WriteLine(letter.EntityMetadata);
}
}
为了满足存储多种类型字母的列表:
abstract class AbstractLetter: ILetter<IEntity>
{
public abstract string EntityMetadata { get; set; }
public abstract List<IEntity> GetRecords();
}
class JobLetter<T> : AbstractLetter where T : IEntity, new()
{
public override string EntityMetadata { get; set; }
public override List<IEntity> GetRecords() { return null; }
public JobLetter()
{
var entity = new T();
EntityMetadata = entity.Name;
}
}
class SubsidyLetter<T> : AbstractLetter where T : IEntity, new()
{
public override string EntityMetadata { get; set; }
public override List<IEntity> GetRecords() { return null; }
public SubsidyLetter()
{
var entity = new T();
EntityMetadata = entity.Name;
}
}
List<T> CreateLetterList<T>() where T : AbstractLetter, new()
{
return new List<T> { new T(), };
}
void Main()
{
var jobLetterList = CreateLetterList<JobLetter<FooEntity>>();
var subsidyLetterList = CreateLetterList<SubsidyLetter<FooEntity2>>();
var mergedList = new List<AbstractLetter>();
mergedList.Add(jobLetterList[0]);
mergedList.Add(subsidyLetterList[0]);
foreach (var letter in mergedList)
{
Console.WriteLine(letter.EntityMetadata);
}
}
如您所见,您将丢失基类中的一些类型信息。
为了支持您尝试实现的目标(但不一定是必需的功能),您需要对类型进行有效的差异:
interface ILetter<out T> where T:IEntity
{
string EntityMetadata { get; set; }
IEnumerable<T> GetRecords();
}
abstract class AbstractLetter<T> : ILetter<T> where T:IEntity
{
public abstract string EntityMetadata { get; set; }
public abstract IEnumerable<T> GetRecords();
}
class JobLetter : AbstractLetter<FooEntity>
{
public override string EntityMetadata { get; set; }
public override IEnumerable<FooEntity> GetRecords() { return null; }
public JobLetter()
{
var entity = new FooEntity();
EntityMetadata = entity.Name;
}
}
class SubsidyLetter : AbstractLetter<FooEntity2>
{
public override string EntityMetadata { get; set; }
public override IEnumerable<FooEntity2> GetRecords() { return null; }
public SubsidyLetter()
{
var entity = new FooEntity2();
EntityMetadata = entity.Name;
}
}
void Main()
{
var jobLetterList = CreateLetterList<JobLetter, FooEntity>();
var subsidyLetterList = CreateLetterList<SubsidyLetter, FooEntity2>();
var mergedList = new List<ILetter<IEntity>>();
mergedList.Add(jobLetterList[0]);
mergedList.Add(subsidyLetterList[0]);
foreach (var letter in mergedList)
{
Console.WriteLine(letter.EntityMetadata);
}
}
IEnumerable 将代替 List,因为它不允许您将值放入其中。
基类定义为:
abstract class AbstractLetter<T> : ILetter<T> where T:IEntity
T
与泛型约束绑定,但约束不是类型签名的一部分(类也不支持 co/contravariance,它仅适用于接口,反正你在这里没有任何)。所以,你的类,对于编译器来说,从继承的角度来看,真的看起来像*):
abstract class AbstractLetter<T> : ILetter<T>
这不会告诉编译器它实现了ILetter<IEntity>
.
只需明确添加该部分:
abstract class AbstractLetter<T> : ILetter<IEntity>, ILetter<T> where T:IEntity
当然,它可能会迫使您添加一些代码来满足该额外的接口(您可以让 VS 为您生成它们并填充当前代码的传递),但它会起作用。
编辑:哦,你可以像ChrisEelmaa明智地建议的那样在界面上使用方差设置(我很惊讶我没有想到它,即使在提到方差之后,呵呵!但这不能直接适用于当前的方法集。我也不建议ILetter<int T> + ILetter2<out T>
创建两个接口 - 这几乎总是会导致膨胀和非常奇怪和复杂的代码。与几个演员相比太复杂了。
*)整个这一段是一个非常非常松散的描述