C#:将子类实例分配给抽象类实现的接口

时间:2016-06-12 12:05:24

标签: c# oop

public interface IParser<T> where T: new()
{
    IList<T> Parse();
}

上面的接口是通过以下抽象类

实现的
public abstract class BaseParser<T>: IParser<T> where T : new()
{
   protected abstract string Sql { get;}
   public List<T> Parse()
   {
      // do parsing
      Console.WriteLine(Sql);
   }
}

以下是上面抽象类的两个具体实现

public class EMailParser: BaseParser<Email>
{
    protected override string Sql
    {
        get
        {
            return @"SELECT * FROM emails";
        }
    }
}

public class UrlParser : BaseParser<Url>
{
    protected override string Sql
    {
        get
        {
            return @"SELECT * From Url";
        }
    }
}

用法:

class Program
{
    static void Main(string[] args)
    {
       if(args[1] == "url")
          Parser<Url>();
       else
          Parser<Email>();
    }
    static void Parse<T>()
    {
       // Create instance based on typof T and then assign to implementaion
       IParser<T> parser = typeof(T) == typeof(Url) ? new UrlParser(): new EmailParser();
       parser.Parse();
    }
}

我想基于EmailParser方法中提供的泛型类型创建UrlParserProgram.Main的实例,并将其分配给BaseParser实现的接口(抽象类)。我怎样才能做到这一点?我知道我可以通过将Program.Parse<T>修改为以下

来解决这个问题
static void Parse<T>() where T: new()
{
    IParser<T> parser = typeof(T) == typeof(Url) ? new UrlParser() as BaseParser<T> : new EmailParser() as BaseParser<T>;
    parser.Parse();
}

但是我想知道为什么我不能将子类实例分配给抽象类实现的接口?

我无法理解为什么以下行不起作用

IParser<T> parser = typeof(T) == typeof(Url) ? new UrlParser(): new EmailParser();

为什么这条线工作

IParser<T> parser = typeof(T) == typeof(Url) ? new UrlParser() as BaseParser<T> : new EmailParser() as BaseParser<T>;

根据@nawfal的回答,这一行也不应该起作用,因为BaseParser和BaseParser是不同的类型。是否存在来自BaseParser的IParser的隐含案例?

3 个答案:

答案 0 :(得分:4)

我认为问题在于编译器不考虑您为其分配结果的类型     ?  解析条件时有条件。相反,?:是独立解析的,因此编译器无法确定要使用的类型。

编辑:来自C#5.0规范的第7.14节:

  

?:运算符的第二个和第三个操作数x和y控制   条件表达式的类型。如果x具有类型X并且y具有类型Y.   然后:

     
      
  • 如果从X到Y存在隐式转换(第6.1节),而不是从Y到X,则Y是条件表达式的类型。

  •   
  • 如果从Y到X存在隐式转换(第6.1节),但是   不是从X到Y,那么X是条件表达式的类型。

  •   
  • 否则,无法确定表达式类型和编译时间   发生错误。

  •   

答案 1 :(得分:2)

因为UrlParserEmailParser之间没有隐式转换。它们分别返回BaseParser<Url>BaseParser<Email>(或IParser<Url>IParser<Email>),就编译器而言,它们都是不同的类型。

我会保留一本字典来保存类型信息的地图,然后使用反射。像(未经测试)的东西:

static Dictionary<string, Type> typeInfos = new Dictionary<string, Type> 
{
    { "url", typeof(Url) },
    { "email", typeof(Email) },
    // and so on
};

而你这样做,

class Program
{
    static void Main(string[] args)
    {
        Parse(args[1]);
    }

    static void Parse(string type)
    {
        var parserType = typeof(BaseParser<>)
            .Assembly // or whatever the assembly is
            .GetTypes()
            .First(t => t.BaseType?.GetGenericArguments().FirstOrDefault() == typeInfos[type]);
        dynamic parser = Activator.CreateInstance(parserType);
        parser.Parse();
    }
}

使用表达式树和/或缓存内容来加快速度。

更新:不,BaseParser<T>BaseParser<T>完全相同。 T在泛型方法中一次只能有一个值。真正的问题是如何投射new UrlParser() as BaseParser<T>。在通用上下文中,可以使用as将任何内容转换为任何内容.C#规则在那里有点自由(我不知道哪个部分完全符合规范)。

答案 2 :(得分:1)

您可以使用动态调度:

static IParser<Url> CreateParser(Url uri)
{
    return new UrlPaqrser<Url>();
}

static IParser<Email> CreateParser(Email mail)
{
    return new EmailPaqrser<Email>();
}

static void Main(string[] args)
{
    dynamic t = new Url();
    var parser = CreateParser(t); // invokes CreateParser(Url uri)

    t = new Email();
    parser = CreateParser(t);  // invokes CreateParser(Email email)
}