C# 操纵父抽象类中的泛型对象(协方差/逆变)

C# 操纵父抽象类中的泛型对象(协方差/逆变),c#,generics,covariance,contravariance,C#,Generics,Covariance,Contravariance,我正在尝试实现一个允许读取和解释文件中的行的系统 我需要管理不同的文件格式。为此,我有一个抽象的导入器类,它以不同的方式继承和实现(基于文件格式) 文件的行可能导致不同的对象,因此我创建了一个通用接口,它知道如何解析行、验证行等:public interface-ILineImporter,其中ObjType:IImportableObject 具体的Importer类通过重写的抽象方法public-abstract-ILineImporter-GetLineImporter(字符串行),知道对

我正在尝试实现一个允许读取和解释文件中的行的系统

我需要管理不同的文件格式。为此,我有一个抽象的
导入器
类,它以不同的方式继承和实现(基于文件格式)

文件的行可能导致不同的对象,因此我创建了一个通用接口,它知道如何解析行、验证行等:
public interface-ILineImporter,其中ObjType:IImportableObject

具体的
Importer
类通过重写的抽象方法
public-abstract-ILineImporter-GetLineImporter(字符串行),知道对给定行使用哪个
LineImporter

问题在于,在该方法的实现中,返回的类型取决于具体的导入程序和行:

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();
}

处理此问题的正确方法是什么?

因为接口确实将
ObjType
作为参数(方法的)获取并返回它(从另一个方法),所以我不能是协变的,也不能是逆变的

我建议您创建一个非泛型的
ILineImporter
接口,它的方法与IIImportableObject一起工作,泛型接口将从IIImportableObject进行extand/inherit

然后,
MyObjectALineImporter
将能够被强制转换为“ILineImporter”(非泛型)

公共接口
{
IIImportableObject GetObject();
布尔IsObjectValid(IIImportableObject o);
}
公共接口ILineImporter:ILineImporter
其中ObjType:IImportableObject
{
ObjType GetObject();
bool IsObjectValid(对象类型o);
}

您可以添加一个不变的接口,该接口继承自:

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);
           }
       }
   }
}

请注意,
ISomeInterface
是不变的,即协变和逆变,因此定义中既没有
in
也没有
out

现在它说'MyObjectALineImporter.GetObject()'无法实现'ILineImporter.GetObject()',因为它没有匹配的返回类型'IImportableObject'。这很奇怪,因为它的父类(ILineImporter)有一个IIImportableObjects类型约束,所以应该对更基本的接口使用显式接口实现。谢谢,现在已经编译好了。但是,它迫使我复制具体LineImporter中的每个方法(一个用于非泛型接口,一个用于泛型),并迫使我将在方法上接收的IIImportableObject参数转换为具体类(我失去了泛型的优势)。有什么想法吗?关于方法复制-不幸的是,这是一个要求,但很小(非泛型实现应该只调用泛型实现。关于导入器的转换,您没有失去泛型的优势,因为有两件事:1.在初始实现中,您尝试将行导入器存储为
ILineImporter
,这与非泛型
ILineImporter
(这没关系,因为进口商现在不需要知道正在使用的进口商的确切行).2.在下一篇评论->中,我简化了我的代码,将其发布到这里,因此实际上我的LineImporter中有两个以上的方法。为了避免代码重复和不必要的强制转换方法,我最终做的是添加一个泛型抽象父LineImporter类,该类显式实现了这两个接口并调用泛型抽象方法。我无法o这是因为我的导入器具体实现可以基于'line'参数返回不同的LineImporter。导入器返回给定行的相应LineImporter。@OlivierPayen:在这种情况下,您需要使用非泛型的“base”界面-
I
不可分配给
I
。这是否破坏了您设计的其他部分?我认为这是Shlomi Borovitz在其回答中建议的,但我仍然有一个问题(阅读他回答中的评论)。
public interface ILineImporter
{
    IImportableObject GetObject();
    bool IsObjectValid(IImportableObject o);
}

public interface ILineImporter<ObjType> : ILineImporter
    where ObjType : IImportableObject
{
    ObjType GetObject();
    bool IsObjectValid(ObjType o);
}
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);
           }
       }
   }
}
public class ConcreteImporter1 : Importer<MyObjectA>
{
   public override ILineImporter<MyObjectA> GetLineImporter(string line)
   {
       return new MyObjectALineImporter();
   }
}