操作父级抽象类中的泛型对象(协方差/变换)

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

我需要管理不同的文件格式。 为此,我有一个抽象的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>

如果我添加inout关键字来协方差/逆变,编译器表明,我的通用接口是不是协变/ 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的定义。

链接地址: http://www.djcxy.com/p/39439.html

上一篇: Manipulate generic objects in parent abstract class (covariance/contravariance)

下一篇: How to cast generic interfaces using contravariant parameters to a base type?