C# No implicit conversion to inherited type

耗尽温柔 提交于 2020-07-22 05:51:09

问题


I'm hoping someone can explain this to me as I'm confused about it. I have a series of inheritance amongst my classes and interfaces, with a factory to instantiated the correct object. However, I am getting a Cannot implicitly convert type 'Conn' to IConn. An explicit conversion exists (are you missing a cast?)

Here is an trimmed down version of the code.

public abstract class Id
{
    public int Id { get; set; }
}

public abstract class Id_SourceId
{
    public int SourceId { get; set; }
}

public interface IConn<T> where T : Id
{
    IEnumerable<T> GetIds();
}

public abstract class Conn<T> : IConn<T> where T : Id_SourceId{ ... }

public class A_Id_SourceId : Id_SourceId{ ... }
public class B_Id_SourceId : Id_SourceId{ ... }

public class ConnA : Conn<A_Id_SourceId > { ... }
public class ConnB : Conn<B_Id_SourceId > { ... }

public IConn<Id_SourceId> Create(bool val)
{
    if (val)
        return new ConnA(); //<-- Error here
    else
        return new ConnB(); //<-- And here
}

From what I can see ConnA is of type Conn<A_Id_SourceId> which in turn (doing a maths type substitution) is of type IConn<A_Id_SourceId> and again, is of type IConn<Id_SourceId>.

I realise an explicit cast (return (IConn<Id_SourceId>)(new ConnA())) would resolve it, but I think its rather ugly, and the cast would be slow (from what I understand). But also I want to understand why it doesn't work.

Thanks.


回答1:


Generic arguments, by default, do not participate in inheritance hierarchies, as doing so would break type safety, however, as your IConn<T> interface only returns objects of type T, and doesn't require them as arguments, you can make the interface covariant, by adding the out keyword:

public interface IConn<out T> : where T : Id
{
    IEnumerable<T> GetIds();
}

By doing so, the compiler is satisfied that more derived generic types can safely be used:

public IConn<Id_SourceId> Create(bool val)
{
    if (val)
        return new ConnA(); //<-- Now fine
    else
        return new ConnB(); //<-- Also fine
}

If your wondering why this is the case, maybe this example will clarify.

Imagine IConn<T> was invariant, and had a method that accepted Id objects:

public interface IConn<T> : where T : Id
{
    IEnumerable<T> GetIds();
    void AddId(Id id);
}

And Conn<T> was implemented like this:

public class Conn<T> : IConn<T>
{
    private List<T> ids = new List<T>();
    public void AddId(T id) => ids.Add(id);
}

And you used ConnA in place of IConn<Id_SourceId>:

IConn<Id_SourceId> connA = new ConnA();

B_Id_SourceId sourceB = new B_Id_SourceId();

// B_Id_SourceId inherits from Id_SourceId

connA.AddId(sourceB); // Tries to add a `B_Id_SourceId` to a list of `A_Id_SourceId`!



回答2:


The problem is that Conn<A_Id_SourceId> does not inherit from Conn<Id_SourceId>, the same way List< String> does not inherit from List< object>

you can add a non-generic interface IConn in this way:

public abstract class Id
{
    public int Id { get; set; }
}

public abstract class Id_SourceId
{
    public int SourceId { get; set; }
}

public interface IConn {
    IEnumerable<Id> GetIds();
}

public interface IConn<T> : IConn where T : Id
{
    IEnumerable<T> GetIds();
}

public abstract class Conn<T> : IConn<T> where T : Id_SourceId{ 
   public IEnumerable<Id> IConn.GetIds() {
        return this.GetIds().Cast<Id>();
   }

   public abstract IEnumerable<T> GetIds();
}

public class A_Id_SourceId : Id_SourceId{ ... }
public class B_Id_SourceId : Id_SourceId{ ... }

public class ConnA : Conn<A_Id_SourceId > { ... }
public class ConnB : Conn<B_Id_SourceId > { ... }

public IConn Create(bool val)
{
    if (val)
        return new ConnA(); //<-- Error here
    else
        return new ConnB(); //<-- And here
}


来源:https://stackoverflow.com/questions/62949603/c-sharp-no-implicit-conversion-to-inherited-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!