Home > Back-end >  Interface downcasting with generics
Interface downcasting with generics

Time:12-29

I have the following code where I want to downcast to an interface with generic but I get Run-time exception: Unable to cast object of type 'FinalAssociator' to type 'IAssociator`1[Common]'.

public interface ICommon 
{
    string Name {get;set;}
}
public class Common : ICommon 
{
    public string Name {get;set;}
}
public class FinalCommon : Common {}
public interface IAssociator<T> where T : ICommon
{
    void HandleEvent(T data);
}
public abstract class Associator<T> : IAssociator<T> where T : ICommon
{
    public abstract void HandleAnotherEvent(T data);
    public void HandleEvent(T data)
    {
        HandleAnotherEvent(data);
    }
}
public class FinalAssociator : Associator<FinalCommon>
{
    public override void HandleAnotherEvent(FinalCommon data)
    {
        Console.WriteLine(data.Name);
    }
}
var x = new FinalAssociator();
var y = new FinalCommon { Name = "John" };
var z = (IAssociator<Common>)x;
z.HandleEvent(y);

CodePudding user response:

You can't do this because it could lead to runtime errors due to invalid types, which is one of the things generics is intended to prevent. Consider what would happen if the compiler allowed your code. You have:

z.HandleEvent(y);

Here y is an instance of FinalCommon, which won't present a problem. However, what if you instead passed in something else, like:

z.HandleEvent(new Common());

This would result in your passing an instance of something that isn't FinalCommon to your method that is definitely expecting an instance of FinalCommon. This would be illegal, and the compiler prevents you from getting into this situation.

CodePudding user response:

FinalAssociatior inherits from Associator<FinalCommon>. Its HandleAnotherEvent method expects an argument of type FinalCommon.

If you could cast an instance of it as IAssociator<Common> then you'd be able to pass an argument of type Common to it, even though the class expects FinalCommon.

var finalAssociator = new FinalAssociator();
var commonAssociator = (IAssociator<Common>)finalAssociator; // can't do this

// You'd be able to do this because the interface allows it, but it doesn't
// make sense because the object is a FinalAssociator
// and it doesn't take this argument.
commonAssociator.HandleAnotherEvent(new Common()); 

As written, the compiler is unable to determine that this is invalid, which is why you get a runtime error. (Resharper provides a warning that this may fail at runtime.)

CodePudding user response:

So first of all I would consider your example shown here an example of generic upcasting, basically you are trying to use a more derived version of a generic parameter, This would work if you do (IAssociator<FinalCommon>)x, but without special keywords we cannot achieve covariance (more derived generic arguments). Using the out keyword to make the generic covariant would solve the problem for this situation however you cannot use covariant generic arguments for input so you could not use the method. So something like this is not really possible, take a look here for an answer on upcasting generic arguments, and here on why inputs don't work with covariant generic arguments.

  • Related