1

I wanted to make the following inheritance with included generics, but the final cast a as A<XBase> always results in null, because the cast is not valid. Could anybody elaborate on why this cast would be invalid, as well as maybe a solution to this problem.

public class XBase {}
public interface A<T> where T : XBase 
{
    //Edited
    void Method(T param);
}


public class Implementor : A<Implementor.ImplementorX > 
{
    public class ImplementorX : XBase {public int a;}

    //Edited
    void Method(ImplementorX param) {}
}

public class HelloWorld
{
    public static void Main(string[] args)
    {
        var a = new Implementor();
        
        var castRes = a as A<XBase>;
        Console.WriteLine(castRes != null);
    }
}

see live example https://rextester.com/BTNVT61833

EDITED: Added a method to interface A<T> bc else it could be solved with @DavidG's response

P. Steininger
  • 172
  • 1
  • 8
  • 1
    You should go learn about covariance/contravariance. You could make this work by changing your interface: `public interface A where T : XBase ` – DavidG Mar 16 '22 at 09:55
  • oh great, but what if I had a method that takes T as a parameter in A? – P. Steininger Mar 16 '22 at 09:58
  • Your code no longer compiles. You need to implement `Method` in `Implementor`. – NetMage Mar 16 '22 at 19:51
  • When you implement `Method`, you will see it takes an `ImplementorX` parameter. An `XBase` is not an `ImplementorX` so you can't do the cast. Try changing the interface method to be `Method(XBase param)` and then you can use `out` and it will work. – NetMage Mar 16 '22 at 19:59
  • The main point of this T in Method was to assure that there could be objects of type ImplementorX be passed inside Implementor. I wanted to have it that way to not be required to perform casting in the Method of the corresponding implementors. Although it seems like this is not possible. – P. Steininger Mar 17 '22 at 13:16
  • Let's say I have two types, `Apple` and `Banana`; both of which implement `IFruit`. Now I create a `BowlOf` collection. If I were able to cast this to `BowlOf` (which is what you're doing with the `a as A`) then I would be able to add a `Banana` to my `BowlOf` - and that's not right and that's why the cast isn't allowed. – Enigmativity Mar 25 '22 at 07:04

1 Answers1

1

If you make an explicit cast:

var castRes = A<XBase>(a);

then you will see the following error:

Unable to cast object of type '' to type '`

Why? In my view, it is better to understand using real world example. I've renamed classes based on this explanation. There are comments which maps explanations to your classes in question.

Abstractions:

// XBase 
public class Animal { }

// class ImplementorX : XBase {public int a;}
public class Bird : Animal
{
    public string WingColor { get; set; }
}

// interface A<T> where T : XBase 
public interface IHat<T> where T : Animal
{
    void Hide(T param);

    T Pull();
}

Concrete implementations:

// class Implementor : A<Implementor.ImplementorX > 
public class Peacock : IHat<Bird>
{
    // void Method(ImplementorX param) {}
    void IHat<Bird>.Hide(Bird param)
    { }

    public Bird Pull()
    { }
}

and how it can be called:

public static void Main(string[] args)
{
    Peacock peacockHat = new Peacock();

    IHat<Animal> animalHat = (IHat<Animal>) peacockHat; // runtime error 'Unable to cast
    // object of type 'HelloWorld.Peacock' to type 'HelloWorld.IHat`1

    // because 
    animalHat.Hide(new Dolphin()); // Hide a dolphin in a peacock hat?  
}

So we cannot hide hat of Peacock from Dolphin. It is not okay. CLR prevents us from making inappropriate actions.

In short:

In short, imagine you have two animals such as Wolf and Sheep. And these classes implements IAnimal interface:

public interface IAnimal
{    }

public class Wolf: IAnimal
{    }

public class Sheep : IAnimal
{    }

So Sheep, Wolf classes implement the inherited interface IAnimal:

            IAnimal
             /  \
            /    \
         Sheep   Wolf 

And then these animals can be put in cage:

public class Cage<T> where T : IAnimal
{
    public void Put(T animal)
    {   }
}

Then you create a cage for Sheep. After that somebody wants to cast Sheep cage to IAnimal:

Cage<Sheep> sheepCage = new Cage<Sheep>();
sheepCage.Put(new Sheep());

Cage<IAnimal> animalCage = (Cage<Wolf>)sheepCage; // compile error
// if there were no error, then you will be able to do:
animalCage.Put(new Wolf()); // it is not good idea
StepUp
  • 36,391
  • 15
  • 88
  • 148