Cast between generic collections - c#

In the following example, why can't I cast collectionA to collectionB given that the compiler knows that a TItem is a A<T>?
public class A<T>
{
}
public void Foo<TItem, T> () where TItem : A<T>
{
var collectionA = new List<TItem>();
var collectionB = (List<A<T>>)collectionA; // "Cannot cast" error here
}

The problem is that it would allow you to place inappropriate items into collectionA.
Here's a simplified reworking of it, which hopefully makes it easier to see the problem:
Suppose you have (pseudocode):
class Animal {...}
class Dog: Animal { Bark(){} }
class Cat: Animal { Meow(){} }
Now imagine you could do this:
var dogs = new List<Dog>();
dogs.Add(new Dog());
dogs[0].Bark();
var animals = (List<Animal>) dogs;
Then you would be able to do this:
animals.Add(new Animal()); // Adds an Animal to the list 'dogs', which 'animals' references.
dogs[1].Bark(); // dogs will now have two elements, but the second isn't a dog -
// so calling Bark() will explode.

I believe it's because you're instructing the system to convert from List<X> to List<Y> rather than saying that you want to cast each item within the list from X to Y.
You can do this though:
public class A<T>
{
}
public void Foo<TItem, T>() where TItem : A<T>
{
var collectionA = new List<TItem>();
var collectionB = new List<A<T>>(collectionA.ToArray());
}

You want to use either
var collectionB = collectionA.OfType<List<A<T>>>();
or
var collectionB = collectionA.Cast<List<A<T>>>();
The first will ignore anything that's not of type List<A<T>> and can't be treated as it. The second will throw an exception if there's something in the list which can't be converted.

Related

Generic overridden method with subclass as return type

Let's say that i have a base class Animal.
public abstract class Animal;
This animal class has the abstract method:
public abstract T Copy<T>() where T : Animal
When this method is overridden in the Lion class:
public class Lion : Animal
{
string roar = "Roar";
}
i wish to return a copy of this lion without its references. So what i think it should be overridden like is this:
public abstract T Copy<T>()
{
return new Lion(){
roar = this.roar;
}
}
but this is not allowed, because Lion cannot be converted to the T type(which is an Animal).
This is a understandable error, becouse not every Animal is necessarily a Lion.
But how can i set this situation up where the copy method knows it supposed to return the subclass, Lion in this case, and not have to do all kinds of casting to the correct type? And still have every animal have a method Copy().
You don't need to use a generic method in this case, because every Lion is an Animal, so you can simply make Copy return an Animal
public abstract class Animal
{
public abstract Animal Copy();
}
public class Lion : Animal
{
public string Roar {get; set;} = "Roar";
public override Animal Copy()
{
return new Lion
{
Roar = this.Roar
};
}
}
Then testing it like so:
public static void Main()
{
var lion1 = new Lion();
var lion2 = lion1.Copy();
Console.WriteLine(lion1 == lion2);
}
Prints false to the console, as expected because C# classes are reference types and the default == implementation checks for reference equals on classes and our two lions aren't the same object in memory.
Now every subclass of Animal must implement the method Copy returning an Animal. Which animal that is doesn't matter
You can not use generics in this situation, they don't support what you want to do.
However, if you're on C# 9 you can use the new "Covariant return types" support.
Specifically, you can write your code like this:
public abstract class Animal
{
public abstract Animal Copy();
}
public class Lion : Animal
{
public override Lion Copy() => new Lion();
// ^
// notice that I used Lion here, not Animal
}
If you're calling Copy through a reference declared as being of type Animal but referencing an instance of Lion, you will get back a value typed to Animal, holding a Lion reference.
However, if you're calling Copy through a reference typed to Lion, you will get back a reference typed to Lion as well.
Example:
Animal a = new Lion();
Animal c = a.Copy(); <-- holding a Lion, but type is Animal
Lion l = new Lion();
Lion l2 = l.Copy(); <-- type is now Lion as well
If you're not on C# 9, a different option would be to make the Copy method know the type of the derived class, by making Animal generic:
void Main()
{
Animal<Lion> a = new Lion();
Lion c1 = a.Copy(); // <-- notice that this is now typed to Lion as well
Lion l = new Lion();
Lion c2 = l.Copy();
}
public abstract class Animal<T> where T : Animal<T>
{
public abstract T Copy();
}
public class Lion : Animal<Lion>
{
public override Lion Copy() => new Lion();
}
This, however, means you always have to declare Animal as the right type of animal so you lose some of the benefits of having this base class.

Implicit conversations from child(derived) class to parent(base) class

suppose I have two following classes
public class Animal
{
public string name { get; set; }
}
public class Cat: Animal
{
public int age { get; set; }
public string type { get; set; }
}
and now I want to convert my derived class "Cat" match property(e.g. Name) to my base class "Animal" with implicit conversion as following way.
Cat cat = new Cat();
cat.name = "diana";
cat.age = 2;
cat.type = "Siamese-American Shorthair";
Animal animal = new Animal();
animal = (Animal)cat;
// or
animal = cat as Animal;
so while doing above coding it will work fine and will get name property implicit in Animal class object but if I check object of Animal class that is animal actually contains the object of Cat class that is cat and not actually get the object of Animal class.
So please help me to come over this situation so I can directly implicit convert my child class property to my matched class property with proper parent class object.
A cat will always be a cat, even if you are only looking at animals. You cannot remove the actual type of an object; casting it to a parent type will only affect the variable you store it to but the underlying object remains the same.
Cat cat = new Cat(); // cat is a cat
Animal catAnimal = cat; // a cat is also an animal
Console.WriteLine(catAnimal.GetType()); // still a cat
Cat newCat = (Cat)catAnimal;
Console.WriteLine(newCat == cat); // the same cat
Console.WriteLine(animal == cat); // the same cat
First, this is not an implicit conversion, but an explicit one.
An implicit conversion example is this:
int x = 123;
string y = "asdf" + x; // this is the implicit conversion.
Second, you can't "uncat" the cat. it will still be a cat even if your reference is of type object,
Third, any object from any class will keep the properties and fields of it's parent class, unless they are declared as private, so casting a cat to an animal to get it's name property is redundant.
So, can such a cast be useful?
The answer is yes, it might, in the following situations:
Your derived class hides base class functionality using the new keyword.
Your derived class explicitly implements an interface, and the implicit implementation is different then the explicit one.
Here are examples of these situations:
Casting an object to it's base class to use a property or method is when the property or method in the derived class is declared as new:
public class Base {
internal virtual string X() {
return "Base";
}
}
public class Derived1 : Base
{
internal new string X()
{
return "Derived 1";
}
}
public class Derived2 : Base
{
internal override string X()
{
return "Derived 2";
}
}
Derived1 a = new Derived1();
Base b = new Derived1();
Base c = new Derived2();
Console.WriteLine("Derived1 as Derived1: "+ a.X()); // Derived1 as Derived1: Derived 1
Console.WriteLine("Derived1 as Base: " + b.X()); // Derived1 as Base: Base
Console.WriteLine("Derived2 as Base: " + c.X()); // Derived2 as Base: Derived 2
See fiddle here
Casting an object to one of the interfaces it implements, when the class overloads the explicit implementation with an implicit one.
public interface IBlabla {
string bla();
}
public class BlaBla : IBlabla
{
public string bla() {
return "implicit";
}
string IBlabla.bla()
{
return "EXPLICIT";
}
}
BlaBla Myclass = new BlaBla();
Console.WriteLine(Myclass.bla()); // implicit
Console.WriteLine(((IBlabla)Myclass).bla()); // EXPLICIT
See fiddle here
You cannot change an objects type. As Yuval Itzchakov already mentioned you need to create an instance of type Animal. You may only create some kind of copy-constructor for your Animal-class that copied all properties and variables from the given Animal to the new one:
public Animal(Animal animal) {
this.Name = animal.Name;
// further variables and properties to reset the state exactly to the state of the given animal
}
Now you can create an instance of type Animal from every derived type like so:
Animal ani = new Animal(cat);
However that still sounds like a design-flaw (propbably an XY-problem) to me. If you need to access the Name-property of your cat you won´t need to cast to its base-type.
If you really wanted to, you could do something along the lines of
internal class Program
{
public class Animal
{
public string Name { get; set; }
}
public class Cat : Animal
{
public int Age { get; set; }
public string Type { get; set; }
}
public static void Main()
{
var cat = new Cat();
cat.Name = "Puss";
var animal = cat.ToBaseClass<Animal, Cat>();
Debug.Assert(!(animal is Cat));
Debug.Assert(animal.Name == "Puss");
}
}
public static class ReflectionHelper
{
public static TBase ToBaseClass<TBase, TDerived>(this TDerived from)
where TBase : new()
where TDerived : TBase
{
var result = new TBase();
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(result))
{
propertyDescriptor.SetValue(result, propertyDescriptor.GetValue(from));
}
return result;
}
}
... but it would be pretty ugly ;)
Thanks to all of you who help me to solve my problem and I take everyone answers and comments very seriously.
I have also find another solutions which may be useful to someone. As this questions is regarding conversion with same property from child to parents class, we can use AutoMapper which can do conversion for any class from child to parent class or parent to child class. For that first you can download or update AutoMapper from nuget at give link and add as reference in your project or application.
https://www.nuget.org/packages/AutoMapper/
Now as per my question if I want to convert property from child to parent class than coding using AutoMapper functionality would be as below.
Mapper.CreateMap<Cat, Animal>();
Animal animal = Mapper.Map<Animal>(cat);
I hope this solutions will work for you as it has solved my issues.

Method that excepts objects of derived class [duplicate]

This question already has answers here:
Convert List<DerivedClass> to List<BaseClass>
(13 answers)
Closed 8 years ago.
In the below example, I can add have a List of type Animal. Since Dog and Cat derive from animal, the List can hold each. However, if I have a method that accepts List<Animal>, you can not pass in a reference List<Dog>. If Dog is derived from animal, why is this not valid? However if i have a method that excepts a parameter of type Object, and all objects derive from Object, it works.
class Program
{
static void Main(string[] args)
{
List<Animal> animals = new List<Animal>();
animals.Add(new Dog() { Color = "Black" }); // allowed since Dog derives from Animal
List<Dog> dogs = new List<Dog>();
dogs.Add(new Dog() { Color = "White" });
Test(animals);
Test(dogs); // not allowed - does not compile
Test2(dogs); // valid as all objects derive from object
}
static void Test(List<Animal> animals) {
// do something
}
static void Test2(object obj) {
}
}
public class Animal {
public String Color { get; set; }
}
public class Dog : Animal { }
public class Cat : Animal { }
Imagine that Test(dogs) did compile. Then imagine that Test() is implemented like follows:
static void Test(List<Animal> animals)
{
animals.Add(new Cat()); // Where Cat is derived from Animal
}
You've just added a cat to a list of dogs... Clearly that can't be allowed. And that's why it's not.
However, if you are only accessing the elements of the list rather than adding to the list, you can declare Test() like so:
static void Test(IEnumerable<Animal> animals)
{
foreach (var animal in animals)
{
// Do something with animal.
}
}
Then you can pass any collection type which implements IEnumerable<T>, such as List<T> and plain arrays.
Call Test(dogs.Cast<Animal>().ToList()). Change parameter type to IEnumerable to skip ToList() which creates a copy.
int is an object. List<Animal> is an object, as is List<Dog>. But List<Dog> is NOT List<Animal>.

WCF Return list of baseclass objects containing derived class

I've got a function returning all snakes. Looks like this and works fine:
// Function that returns JSON list of snakes
public List<Snake> GetSnakes()
{
var snakes = new List<Snakes>();
snakes.Add(new Snake { Length = "10.2" } );
return snakes;
}
Now I got a bunch of animals with different properties, and I dont want to make a list for each one of them. I would like something like this:
public class AnimalService : IAnimalService
{
private List<Animal> animals = new List<Animals>();
public List<Animal> getSnakes()
{
animals.Add(new Snake { Name = "Snake" } );
return animals;
}
public List<Animal> getPigs()
{
animals.Add( new Pig { Weight = "100" } );
return animals;
}
}
But this doesnt work. When I add a derived class to my animal list, the WCF service stops producing JSON for that function and returns nothing. No errors or anything but no results. How can I achieve what I want? One list containing return set of animals, doesn't matter what type.
Try adding a KnownTypeAttribute to your Animal class for each subclass you'd like to have serialized:
[DataContract]
[KnownType(typeof(Snake))]
[KnownType(typeof(Pig))]
public class Animal
{
}

Covariance issue

I'm facing a bit of problem with the following casting:
class A
{
}
class B : A
{
}
class C<T> where T : A
{
protected T property { get; set; }
}
class D : C<B>
{
}
class MainClass
{
public static void Main (string[] args)
{
C<A> x = new D();
// Error CS0029: Cannot implicitly convert type `SampleApp.D' to `SampleApp.C<SampleApp.A>' (CS0029) (SampleApp)
}
}
I don't understand why this is failing since D is wider than C<A> since it implements C<B>, and B : A. Any workarounds?
If you can use C# 4.0, you can write the following code.
class A { }
class B : A {}
interface IC<out T> {}
class C<T> :IC<T> where T : A { protected T property { get; set; } }
class D : C<B> {}
class MainClass {
public static void Main()
{
IC<A> x = new D();
}
}
Let's name your classes Animal for A, Barker for B, and Dog for D.
Actually C<Animal> is wider than Dog : C<Barker>. Assume you have public property Me of type T and assignment possible:
C<Animal> a = new Dog();
a.Me = Elephant; // where Elephant inherited from Animal
Oops! Dog is parametrized with Barker. Have you seen barking elephants?
You need to declare some covariant interface to allow assignment of class instantiated with more derived type argument C<Barker> to object instantiated with less derived type argument C<Animal>. You can use empty interface, like #NickW suggested, but you will not be able to do something with instance of that interface (it's empty!). So, let's do something like that:
interface IC<out T>
where T : Animal
{
IEnumerable<T> Parents(); // IEnumerable is covariant
T Me { get; } // no setter
}
class C<T> : IC<T>
where T: Animal
{
// implementation
}
class D : C<Barker>
{
// implementation
}
Above scenario is still impossible, but now you can
IC<Animal> a = new Dog();
foreach(var parent in a.Parents)
Console.WriteLine(parent);
Console.WriteLine(a.Me);
You can't do that because the Generics are actualy templates and they don't act like what you want to do with them. Let me show you by this:
When you say "C<A>" it means a generic class by a "parameter" of "A".
BUT
When you say "D" it means exactly "D"!
So D is not equal to a generic class by a parameter of A. As you can simply see it in the result of ToString function on both types (by using typeof).
Hope it helps
Cheers

Categories

Resources