操作父级抽象类中的泛型对象(协方差/变换)
我试图实现一个允许读取和解释文件中的行的系统。
我需要管理不同的文件格式。 为此,我有一个抽象的Importer
类,它是继承和实现不同的(基于文件格式)。
一个文件的行可能导致不同的对象,所以我创建了一个通用接口,知道如何解析行,验证它等: public interface ILineImporter<ObjType> where ObjType : IImportableObject
具体的Importer
类通过覆盖的抽象方法知道哪个LineImporter
用于给定的行public abstract ILineImporter<IImportableObject> GetLineImporter(string line);
。
问题在于,在实现此方法时,返回的类型取决于具体的Importer
和行:
public override ILineImporter<IImportableObject> GetLineImporter(string line)
{
// TODO: Return the appropriate LineImporter for the given line
// For the example, I always return a MyObjectALineImporter, but it can potentially be any ILineImporter<IImportableObject>
return new MyObjectALineImporter();
}
这不会编译,因为编译器不能隐式地将MyObjectALineImporter
转换为ILineImporter<IImportableObject>
。
如果我添加in
或out
关键字来协方差/逆变,编译器表明,我的通用接口是不是协变/ contravariantly有效。
以下是您可以在标准控制台应用程序中重现此问题的简化源代码:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ConsoleApplication2
{
public interface IImportableObject { }
public class MyObjectA : IImportableObject { }
public interface ILineImporter<ObjType> where ObjType : IImportableObject
{
ObjType GetObject();
bool IsObjectValid(ObjType o);
}
/// <summary>
/// Concrete class that knows how to get the appropriate MyObjectA instance, validate it, etc.
/// </summary>
public class MyObjectALineImporter : ILineImporter<MyObjectA>
{
public MyObjectA GetObject()
{
Console.WriteLine("GetObject");
return new MyObjectA(); // For the example, I create a new instance but this method can potentially return an existing object from DB.
}
public bool IsObjectValid(MyObjectA o)
{
Console.WriteLine("IsValid");
// TODO : Test if the object is valid
return true;
}
}
public abstract class Importer
{
public abstract ILineImporter<IImportableObject> GetLineImporter(string line);
public void Importe(string text)
{
using (StringReader reader = new StringReader(text))
{
string line;
while ((line = reader.ReadLine()) != null)
{
var lineImporter = this.GetLineImporter(line);
var obj = lineImporter.GetObject();
bool isValid = lineImporter.IsObjectValid(obj);
}
}
}
}
public class ConcreteImporter1 : Importer
{
public override ILineImporter<IImportableObject> GetLineImporter(string line)
{
// TODO: Return the appropriate LineImporter for the given line
// For the example, I always return a MyObjectALineImporter, but it can potentially be another ILineImporter
return new MyObjectALineImporter();
}
}
class Program
{
static void Main(string[] args)
{
Importer importer = new ConcreteImporter1(); // TODO : Retrieve the appropriate Importer with a Factory.
importer.Importe(string.Empty);
Console.ReadKey();
}
}
}
什么才是解决这个问题的正确方法?
因为接口确实将ObjType
作为(方法的)参数并且返回它(来自另一个方法),所以我不能是协变的,也不是逆向的。
我建议你创建一个非泛型ILineImporter
接口,它的方法与IImportableObject一起工作,泛型接口将继承/继承它。
然后MyObjectALineImporter
将能够被转换为'ILineImporter`(非通用的)。
public interface ILineImporter
{
IImportableObject GetObject();
bool IsObjectValid(IImportableObject o);
}
public interface ILineImporter<ObjType> : ILineImporter
where ObjType : IImportableObject
{
ObjType GetObject();
bool IsObjectValid(ObjType o);
}
您可以添加一个Importer
继承的不变界面:
public interface ISomeInterface<TObject> where TObject : IImportableObject {
ILineImporter<TObject> GetLineImporter(string line);
}
public abstract class Importer<T> : ISomeInterface<T> where T: IImportableObject
{
public abstract ILineImporter<T> GetLineImporter(string line);
public void Importe(string text)
{
using (StringReader reader = new StringReader(text))
{
string line;
while ((line = reader.ReadLine()) != null)
{
var lineImporter = this.GetLineImporter(line);
var obj = lineImporter.GetObject();
bool isValid = lineImporter.IsObjectValid(obj);
}
}
}
}
您的ConcreteImporter1
然后继承一个类型参数化的Importer
:
public class ConcreteImporter1 : Importer<MyObjectA>
{
public override ILineImporter<MyObjectA> GetLineImporter(string line)
{
return new MyObjectALineImporter();
}
}
需要注意的是ISomeInterface
是不变的,即双方合作和逆变,所以既没有in
也不out
的定义。
上一篇: Manipulate generic objects in parent abstract class (covariance/contravariance)
下一篇: How to cast generic interfaces using contravariant parameters to a base type?