I have a simple class structure
abstract class A {
List<A> containerList;
...
}
class B : A {....}
class C : A {....}
I make sure that the containerList contains only elements of class B or C (as the list is filled in these child classes and A is abstract).
Now, I'd like, somehow, to have a base property / method in A which would cast itself to whatever the real type of the object is, i.e. B or C. Having an abstract method is just fine, but it is important to get a Collection<T> of B or C objects, as I have bindings that will depend on it.
Now, I'd like, somehow, to have a base property / method in A which would cast itself to whatever the real type of the object is, i.e. B or C.
I think you're mixing casting and converting. A Cast is a compile-time construct that just determines how methods and properties are bound at compile-time. It does not change the underlying object.
There's no conversion necessary because the object is already either a B or a C - since A is abstract you can't have an object that is actually an A.
So a method that returns the underlying collection as either a collection of Bs or Cs would just be:
public IEnumerable<B> GetBs()
{
return containerList.OfType<B>();
}
public IEnumerable<C> GetCs()
{
return containerList.OfType<C>();
}
If you want one generic method where the caller determines the type, you can do:
public IEnumerable<T> GetTs<T>() where T:A
{
return containerList.OfType<T>();
}
You could use generics like that (though it looks a little strange):
public abstract class A<T> where T : A<T>
{
protected List<A<T>> containerList;
public Collection<T> ContainerList
{
get { return new Collection<T>(containerList.OfType<T>().ToList()); }
}
}
public class B : A<B>
{
//...
}
public class C : A<C>
{
//...
}
But since containerList should only contain elements of the derived type, you can make it completely generic too like that:
protected List<T> containerList;
and could ommit the OfType() call:
public Collection<T> ContainerList { get { return new Collection<T>(containerList); }}
One drawback is that someone could have the idea to make a class D like that:
public class D : A<B>
{}
and would now have a class D with a ContainerList of type Collection<B>. But if it's that what he wants...
Instead of casting the objects in the list, you could use the is keyword (documentation link). When you need to operate on your List, just ask before doing your operation if (containerList[i] is B).
Related
I am new to C#. So please bear with me. I ran into a problem when I tried to compare two lists of different derived objects. I have a base class and a child class that inherits from it. The derived class has additional properties in the Equals() value comparison. I implement the IEquatable<> interface in the bass class as well as the derived class. I then fill two lists, each with one object from the derived class. The problem is when I call the Equals() function, it is the Base.Equals() that gets called. So basically the items in the list are treated as base class objects instead of the derived objects. How do I get out of this? See below code:
public class Base : IEquatable<Base>
{
...
public int BaseProperty {get; set;}
...
public virtual bool Equals(Base other)
{
...
return this.BaseProperty == other.BaseProperty;
}
}
public class Derived : Base, IEquatable<Derived>
{
public int DerivedProperty {get; set:}
...
}
public class myList : IList<Base>, IEquatable<myList>
{
private List<Base> _list;
...
public bool Equals(myList other)
{
...
return ENumerable.SequenceEqual(this._list.OrderBy(r => r), other._list.OrderBy(r = > r));
}
}
Main()
{
Derived first = new Derived() {...};
Derived second = new Derived() {...};
myList<Derived> list1 = new List<Derived>(){ first };
myList<Derived> list2 = new List<Derived>(){ second };
bool ret = list1.Equals(list2);
}
The comparison of the two lists is done using SequenceEqual. When I stepped into the calls, I could see the trace ended up in Base.Equals(Base other) instead of Derived.Equals(Derived other). I can't cast it because in reality I have multiple derived classes and the lists could potentially hold any numbers of any objects. So my question is is there a way to make C# respect the derived type(s) in this situation? Thanks.
To try and answer your ultimate question -
So my question is is there a way to make C# respect the derived type(s) in this situation? Thanks.
The reason that the Base.Equals method (rather than the Derived.Equals method) is called is because of your declaration of 'myList' actually defines the Base class -
public class myList : IList<Base>, IEquatable<myList>
Instead, if you implement the myList in a generic way, then there will be no implicit conversion to Base when creating a myList object using the Derived class -
public class myListGeneric<T> : IList<T>, IEquatable<myListGeneric<T>> where T : class
I have tried as best as I can to implement an example for you on the following link (albeit inheriting from generics rather than their interfaces) to try and show you it working. It does differ slightly from what you have above, but it hopefully answers the question.
https://dotnetfiddle.net/VhWTCq
Hope that helps - copy it, step through, and have a go.
not sure if this maybe is a codeReview post but here we go:
My goal is to re-implement the way objects are copied within our application. We have multiple base classes:
CoreList<T> // for all list classes
BasicReference // for all reference classes
CoreObject // for all "normal" domain objects
All classes inherit from these base classes. Right now the copy method is implemented on the CoreObject class and will go through the object tree via reflection, looking at each property type and select the correct way to copy the type and finally returning always CoreObject.
There are some problems which I don't like about that approach, which is why I would like to change it:
After copying an domain object you always have to cast it "back" to the original type, for example: Animal = animal.Copy() as Animal;
All logic to copy each type is within the CoreObject class even though it should not know about other base classes.
So my first attempt was to introduce a interface:
public interface IObjectCopy<out T>
{
T Copy();
}
Which then should be implemented on all base classes. Then every class is responsible for the way it is copied. For example (pseudo code):
public class CoreObject : IObjectCopy<CoreObject>
{
public virtual GerCoreObject Copy()
{
foreach (var prop in properties)
{
if (prop.IsNoSimpleType)
{
(prop as IObjectCopy).Copy()
}
}
}
That solves the copy-responsibility problem, in addition inherited classes can take care of the copy logic themselves.
Unfortunately that does not solve the return type, I still have to cast it to the correct type. I did not think of a better solution to solve this. Any ideas?
This problem could be solved in OO using covariant return types. Unfortunately C# does not support covariant return types like Java and C++, requiring it to always break type safety.
Without breaking type safety (casting) in C# this is unfortunately not possible.
Here are two possible options:
//explicit interface implementation
public class Animal : CoreObject, IObjectCopy<Animal>
{
Animal IObjectCopy<Animal>.Copy()
{
return (Animal) base.Copy();
}
}
//does not require an explicit cast
IObjectCopy<Animal> animalCopy = myAnimal;
Animal copiedAnimal = animalCopy.Copy();
//second option: shadow the original method and cast inside the object
public class Animal : CoreObject, IObjectCopy<Animal>
{
public new Animal Copy()
{
return (Animal) base.Copy();
}
}
Animal copy = myAnimal.Copy();
Another option using bounded quantification:
public class CoreObject : IObjectCopy<CoreObject>
{
public CoreObject Copy()
{
return Copy<CoreObject>();
}
protected T Copy<T>()
where T : CoreObject, new()
{
T t = new T();
//implement copy logic:
return t;
}
}
public class Animal : CoreObject, IObjectCopy<Animal>
{
public new Animal Copy()
{
return Copy<Animal>();
}
}
If I understood it correctly, you need Curiously recurring template pattern
public class BaseClass<T> where T : BaseClass<T>
{
public virtual T Clone()
{
// Perform cloning with reflection.
return clone as T;
}
}
Then you just define your class as:
public class EndObject : BaseClass<EndObject>
{
}
EndObject e;
e.Clone() // Will return EndObject type
I have the following setup and it seems that my object cannot be converted to the generic type. While it is actually the base class. Why doesn't this work? It seems so logical to me.
public class AList<T> where T : A
{
void DoStuff(T foo)
{
}
void CallDoStuff()
{
DoStuff(new A()); // ERROR: Cannot convert A to T
}
}
public class A
{
}
The problem here is that the constraint says that T must be A or a class derived from A.
Now, when you instantiate AList with a concrete type, T is a very specific class. And if you didn't instantiate AList with A itself but with a subclass of it, T is a subclass of A.
You can't convert an instance with a runtime type of the base class to one of its subclasses, as it misses all the information that is being added by the subclass.
Example:
public class Animal
{
public int Foo { get; set; }
}
public class Cat : Animal
{
public int Bar { get; set; }
}
Derived d = new Base();
Would you expect that code to work? Surely not, because a Cat is also a Animal but a Animal is not a Cat.
If you would expect the above code to actually work, ask yourself what is supposed to happen when the following code is executed: d.Bar = 42;
Animal doesn't contain a definition for Bar.
The same is happening in your code - it's just a little bit more obscured with the generics in the game.
T could be also a class that derives from A so you can't put instance of A as a parameter of type T. Something like invoking method, that takes int with and argument that is of type object.
This is the same reason that is an object can not be converted to int implicitly.
The method expects a child class and you are passing the parent, so you need an explicit cast.
Because you are asking T to extend A. So you can replace T with A but not A with T.
if Cat : Animal it doesn't mean you can always convert an Animal to a Cat.
Try and use Activator to give you an instance of A from T because you know that T has to be A due to your constraint, so there isn't a need to use new A() anywhere as you have T, so you can just create an instance of T.
T obj = Activator.CreateInstance<T>();
DoStuff(obj);
Or do a cast of your object as other answers have mentioned, however this will not work in all cases.
T obj = (T)new A();
DoStuff(obj);
Why is this not possible?
abstract class A
{
public abstract T f<T>();
}
class B<T> : A
{
public override T f()
{
return default (T);
}
}
Errors:
does not implement inherited abstract member 'A.f<T>()'
no suitable method found to override
I know that the signature must be same, but from my point of view I see no reason what could possibly be wrong that this is forbidden.
Also I know that another solution is to make A generic, rather than its method, but it is not suitable for me for some reason.
This is not possible because those methods have different signatures. A.f is generic method and B.f is not (it merely uses class generic argument).
You can see this form caller perspective:
A variableA = new A();
variableA.f<int>();
B<int> variableB = new B<int>();
variableB.f();
B does not fulfil the contract of A.
A allows f to be called with any type parameter to return that type. B doesn't allow f to be called with a type parameter, and just returns the type of B's type parameter.
For example, say you had a B<int> and cast it to an A (which should be possible as it inherits from it). Then you called f<bool>() on it (which should be possible as it's an A). What then? The underlying B<int> doesn't have a method to call.
B b = new B<int>();
// This is legal as B inherits from A
A a = b;
// This is a legal call, but how does b handle it?
bool result = a.f<bool>();
In the case of your code
abstract class A
{
public abstract T f<T>();
}
class B<T> : A
{
public override T f()
{
return default (T);
}
}
what do you expect to be called in the below code
public void Foo(A myObj) {
myObj.f<DateTime>();
}
Foo(new B<int>());
There's no implementation for that method though the type contract (the abstract class A) clearly states that you need an implementation. So you can either implement or change the contract to use a type argument at the class level
abstract class A<T>
{
public abstract T f();
}
class B<T> : A<T>
{
public override T f()
{
return default (T);
}
}
does compile however it also limits f of course
Probably this is what you intend to do:
abstract class A
{
public abstract T f<T>();
}
class B<U> : A
{
public override T f<T>() //also needs to have a generic type parameter
{
throw new NotImplementedException();
}
public U f()
{
return f<U>();
}
}
The generic method type parameter and the generic class type parameter (here T and U) have no straightforward connection, i.e. T is not restricted to be U (or something) in the base class and you cannot change this restriction in the derived class.
abstract class A
{
public abstract T f<T>();
}
class B<T> : A
{
public override T f<T>()
{
return default (T);
}
}
I want to create a set of classes that are very similar and can be cast to each other types. My idea was that I would create an Interface object and implement it through a base class. Then create additional classes that inherit from my base. I could then use the Interface to work with the common (base) methods and cast an object from the BASE object to a custom type.
interface ImyInterface {
}
public class MyBase : ImyInterface {
}
public class MyCustom1 : MyBase {
}
public class MyCustom2 : MyBase {
}
// in helper class
public static MyBase GetGeneralOjbect() {
// get a generic base object
return new MyBase();
}
// How I'm trying to use this
MyCustom1 obj = GetGeneralOjbect() as MyCustom1;
This seems to work except for the casting of the object statement. MyCustom1 is always null even though the static helper GetGeneralOjbect returns a good MyBase object. Maybe this can't be done or I'm not doing it correctly. Any input would be appreciated.
This is because you can cast a MyCustom1 or MyCustom2 to MyBase, but not necessarily the other way.
When you create a MyBase via MyBase b = new MyBase();, b is a MyBase but not a MyCustom2, so casting b to MyCustom2 will fail.
What you can do is:
MyBase b = new MyCustom2();
MyCustom2 c = b as MyCustom2();
What you can't do is:
MyBase b = new MyCustom2();
MyCustom1 c = b as MyCustom1();
The "as" keyword says "if this object which is statically typed as MyBase has a runtime type of MyCustom1, then give it back to me statically typed as MyCustom1; otherwise, give me a null reference". The object you are casting has a runtime type of MyBase, not MyCustom1, which is why you are getting a null reference.
Basically you can cast up an inheritance chain but not down it. Say you had the following class heirarchy:
public class A {
}
public class B : A {
}
public class C : B {
}
If you instantiated a new instance of type B you could cast it to A but not C.
Have you considered using Factory Pattern?
An instance of MyCustom1 can be used whenever an instance of MyBase is expected, but MyBase cannot be used when MyCustom1 is expected.