问题
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