How exactly does this work?
If I have this base class
public class BaseClass
{
public int Value1 { get; set; }
public int Value2 { get; set; }
public BaseClass SimpleClone()
{
BaseClass result = new BaseClass();
result.Value1 = this.Value1;
result.Value2 = this.Value2;
return result;
}
}
And this child class
public class DerivedClass : BaseClass
{
public bool Value3 { get; set; }
public DerivedClass()
{
Value3 = true;
}
}
How can I downcast BaseCast.SimpleClone() into anotherObj? What would happen to Value3?
While knowing what happens is good, I am also interested in why it works this way.
If I understand correctly your question is What happens when you do the following
DerivedClass derived = (DerivedClass)baseObj.SimpleClone();
Did you try that? Simply it will result in InvalidCastException since BaseClass is not DerivedClass.
I have answered a similar question here, That should clear things up.
The question as it stands does not make sense. There is no possibility for downcasting, because your clone method already returns the base class. What I think you do (should) want here is override the clone method in your subclass:
public class DerivedClass : BaseClass
{
//... Other stuff
public BaseClass SimpleClone()
{
var result = new DerivedClass
{
Value1 = this.Value1,
Value2 = this.Value2,
Value3 = this.Value3,
}
return result;
}
Now, if you have an object of type DerivedClass and call clone on it, the compiler does not know that the returned object is of type DerivedClass, because of the method signature. But you do, so in that case you can cast the object. All it does is tell the compiler: 'I know better, you think this is a BaseClass but it actually is a DerivedClass'. It has no runtime impact because you don't change the type of the object, you only provide the compiler with extra information.
The answer to your downcast is no, it can't be done as per SriRam's answer. Although rather messy, you could achieve your cloning by using polymorphicism. You will probably need to split out the concerns of creation and assignment when doing so:
public class BaseClass
{
public int Value1 { get; set; }
public int Value2 { get; set; }
protected virtual BasedClass Create()
{
return new BaseClass();
}
public virtual BaseClass SimpleClone()
{
var clone = Create(); // The appropriate create will be called
clone.Value1 = this.Value1;
clone.Value2 = this.Value2;
return clone;
}
}
public class DerivedClass : BaseClass
{
public bool Value3 { get; set; }
public DerivedClass()
{
Value3 = true;
}
protected override BasedClass Create()
{
return new DerivedClass();
}
public override BaseClass SimpleClone()
{
var result = base.SimpleClone();
(result as DerivedClass).Value3 = this.Value3;
}
}
It doesn't work that way. Casting is way of telling the compiler to treat the object as if it was the object of casting class, and you are aware of information loss.
Just because a derived class is derived from base class doesn't necessarily mean that both are same.
So, you will get InvalidCastException.
Related
Is it possible to add different type of generic objects to a list?. As below.
public class ValuePair<T>
{
public string Name { get; set;}
public T Value { get; set;
}
and let say I have all these objects...
ValuePair<string> data1 = new ValuePair<string>();
ValuePair<double> data2 = new ValuePair<double>();
ValuePair<int> data3 = new ValuePair<int>();
I would like to hold these objects in a generic list.such as
List<ValuePair> list = new List<ValuePair>();
list.Add(data1);
list.Add(data2);
list.Add(data3);
Is it possible?
In general, you'd have to either use a List<object> or create a non-generic base class, e.g.
public abstract class ValuePair
{
public string Name { get; set;}
public abstract object RawValue { get; }
}
public class ValuePair<T> : ValuePair
{
public T Value { get; set; }
public object RawValue { get { return Value; } }
}
Then you can have a List<ValuePair>.
Now, there is one exception to this: covariant/contravariant types in C# 4. For example, you can write:
var streamSequenceList = new List<IEnumerable<Stream>>();
IEnumerable<MemoryStream> memoryStreams = null; // For simplicity
IEnumerable<NetworkStream> networkStreams = null; // For simplicity
IEnumerable<Stream> streams = null; // For simplicity
streamSequenceList.Add(memoryStreams);
streamSequenceList.Add(networkStreams);
streamSequenceList.Add(streams);
This isn't applicable in your case because:
You're using a generic class, not an interface
You couldn't change it into a generic covariant interface because you've got T going "in" and "out" of the API
You're using value types as type arguments, and those don't work with generic variable (so an IEnumerable<int> isn't an IEnumerable<object>)
Not unless you have a non-generic base-type ValuePair with ValuePair<T> : ValuePair (it would work for an interface too), or use List<object>. Actually, though, this works reasonably:
public abstract class ValuePair
{
public string Name { get; set; }
public object Value
{
get { return GetValue(); }
set { SetValue(value); }
}
protected abstract object GetValue();
protected abstract void SetValue(object value);
}
public class ValuePair<T> : ValuePair
{
protected override object GetValue() { return Value; }
protected override void SetValue(object value) { Value = (T)value; }
public new T Value { get; set; }
}
No, it is not possible. You could create, in your case, a base class ValuePair from which ValuePair<T> derives. Depends on your purposes.
it's not possible as far as I know.
the line:
List<ValuePair> list = new List<ValuePair>();
you wrote in your sample is not providing a concrete type for T and this is the issue, once you pass it, you can only add object of that specific type.
is there a method to tell a method which type a generic has? what i want to do is to tell the method it can be only an object of type A or B but nothing else, so i can work within like
if (myObject.GetType() == typeof(myTypeA)){doAstuff();}
if (myObjectGetType() == typeof(myTypeB)) {doBstuff();}
method<T>(T myObject){ T = myTypeA, T = myTypeB, T = nothing else}
thanks for any help
You could check for the type inside the method, then cast it to the appropriate type and do the appropriate "stuff":
public void method<T>(T myObject)
{
if (myObject is myTypeA)
{
myTypeA objA = myObject as myTypeA;
objA.DoA_Stuff();
}
else if (myObject is myTypeB)
{
myTypeB objB = myObject as myTypeB;
objB.DoB_Stuff();
}
else
{
return ;
}
}
But that would be a waste of generics. If they share some methods you could also make a base class, and let typeA and typeB inherit from it. Then your method could take a base class object as parameter:
public void method(BaseClass myObject)
and there would be only one if - case and one casting. Only the one with more methods then the base class.
EDIT:
Imagine you would have such a structure:
public class BaseType
{
public int SharedProp { get; set; } // shared property
public virtual int DoSharedStuff() // shared method
{
return SharedProp;
}
}
public class myTypeA : BaseType
{
public int A_Prop { get; set; }
// overwritten shared meth adjusted to the needs of type A
public override int DoSharedStuff()
{
return base.SharedProp + this.A_Prop;
}
}
public class myTypeB : BaseType
{
public int B_Prop { get; set; }
// overwritten shared meth adjusted to the needs of type B
public override int DoSharedStuff()
{
return base.SharedProp + this.B_Prop;
}
// individual method of Type B
public int DoB_Stuff()
{
return this.B_Prop;
}
}
Then you method would take only one of the children of the base class and execute according to the needs:
public void method(BaseType myObject)
{
// shared case: here type A will perform type A action
// and type B will perform type B action
myObject.DoSharedStuff();
// case B where you need really the extra stuff!
if (myObject is myTypeB)
{
myTypeB objB = myObject as myTypeB;
objB.DoB_Stuff();
}
}
This approach or phenomenon is called Polymorphism
You can restrict the allowed types for a gernic with the where command:
public void Test<T>(T param) where T : TypeA {
...
}
https://learn.microsoft.com/de-de/dotnet/csharp/language-reference/keywords/where-generic-type-constraint
But this are only simple constraints so it does not solve the problem for two classes but for this case you can use method overloading:
public void Test(TypeA param) {
...
}
public void Test(TypeB param) {
...
}
If you have only two classes I think that is the best solution because generics would have no benefits.
QUESTION IS CLARIFIED
new thread : subclass properties accessed in generic method with superclass input
I have a base class A
subClasses B, C, and D inherit from A.
Each subClass has 3 subClasses a, b, and c.
a, b, and c have a list of unique properties.
However, now I want to build a generic function to access those properties, so how would I do this without switching on Type?
Clarification : I do not want a : B to have abstract C methods/properties
Example:
public void Method(A a){
if(a.isSubClassOf(B))
{Console.WriteLine(a.BaProperty);}
if(a.isSubClassOf(C))
{Console.WriteLine(a.CbProperty);}
if(a.isSubClassOf(D))
{Console.WriteLine(a.DcProperty);}
}
You can't define a member in derived class and access it via the reference to base class without casting to derived class:
class A {}
class B
{
public int i;
}
A a = new B();
a.i = 0; // error
((B)a).i = 0; // OK
Either declare virtual property in any of base types in your hierarchy, or use casting to the concrete derived type. Of course, in the second case your method doesn't make any sense.
Typically, you would use a virtual or abstract method defined in A and overridden in the subclasses.
public abstract class A
{
protected abstract PropertyType PropertyValue {get;}
public void Method()
{
Console.WriteLine(PropertyValue);
}
}
public class B : A
{
protected override PropertyType Property { get { return PropertyType.B; } }
}
// etc...
An elegant solution is to override ToString
public abstract class A { }
public class B : A {
public int b { get; set; }
public override string ToString()
{
return b.ToString();
}
}
// Do the same with C and D ....
A[] array = new A[] { new B(), new C(), new D() };
foreach (A a in array) {
Console.WriteLine(a);
}
Note that Console.WriteLine does not need to know about a special method or property in A. It also works for types not deriving from A.
It depends greatly in what you really want to achieve. In some cases what Steve Czetty suggests is the best option.
In others you could just keep all the properties different and have a virtual method in the base class that returns for example in this case a "string" that you can then write in the console or anything you wish.
Edit: You could override ToString as Olivier suggested. But only if you feel what yo are going to retun is the "String representation of the object".
public abstract class A
{
public string PropertyA { get; set; }
public virtual string GetString() //
{
return PropertyA;
}
}
public class B:A
{
public string PropertyB { get; set; }
public override string GetString()
{
return PropertyB;
}
}
public class C:A
{
public string PropertyC { get; set; }
public override string GetString()
{
return string.Format("{0} - {1}", base.GetString(), PropertyC) // for example if you wanted to do something more complex
}
}
Now if what you need can not be solved like this, you could cast as Dennis Suggested.
There is another posibility: you could use the visitor pattern. Here you can find several ways to implement it.
Just so you have an idea you would wind up having a class similar to this: (it will depend on what you really want to achieve)
You have to implement some other basic things (interface and some methods), but from a Reference to the base class you will be able to call the corresponding "Visit" method easily. There is a lot of detail in the link i added.
class ClassicVisitor : IAVisitor
{
public string SuperComplexStringResult { get;set; }
public void Visit(B b) { SuperComplexStringResult = String.Format("Super Complex Stuff + {0}", b.PropertyB); }
public void Visit(C c) { SuperComplexStringResult = String.Format("Super Complex Stuff + {0}", c.PropertyC); }
}
public abstract class ContentManagedEntity
{
public Guid Guid { get; set; }
public bool Active;
public int DisplayOrder;
}
public class StoreCategory : ContentManagedEntity
{
public string Name { get; set; }
}
public class XMLStoreCategory : StoreCategory, IXMLDataEntity
{
public bool Dirty = false;
}
void main() {
var storecategory = new StoreCategory { Name = "Discount Stores" };
var xmlstorecategory = (XMLStoreCategory) storecategory; // Throws InvalidCastException
}
Is there a reason it throws an InvalidCastException at runtime on the last line?
(Bah, as I wrote this, the answer popped into my head, clear as day. Posting it up for posterity, and just to make sure I have it right.)
You're asking this:
class Animal { }
class Cat : Animal { }
class ShortHairedCat : Cat { }
ShortHairedCat shortHairedCat = (ShortHairedCat)new Cat();
Is a Cat a ShortHairedCat? Not necessarily. In this particular case, new Cat() is a Cat that is not a ShortHairedCut so of course you get a runtime exception.
Remember, inheritance models is a relationships. It is not necessarily the case that a Base is a Derived, so in general, "downcasting" is dangerous.
All XMLStoreCategory objects are StoreCategorys, but not all StoreCategorys are XMLStoreCategorys. In this case you're creating a StoreCategory and trying to cast it into something it's not.
You instantiated the object as StoreCategory. It's not the same as XMLStoreCategory, so you can't cast it that way.
The case where the cast would work is something like this:
StoreCategory storecategory = new XMLStoreCategory { Name = "Discount Stores" };
var xmlstorecategory = (XMLStoreCategory) storecategory;
That will work, but in your particular case is somewhat useless. Just instantiate XMLStoreCategory and you'll be good to go.
Is it possible to add different type of generic objects to a list?. As below.
public class ValuePair<T>
{
public string Name { get; set;}
public T Value { get; set;
}
and let say I have all these objects...
ValuePair<string> data1 = new ValuePair<string>();
ValuePair<double> data2 = new ValuePair<double>();
ValuePair<int> data3 = new ValuePair<int>();
I would like to hold these objects in a generic list.such as
List<ValuePair> list = new List<ValuePair>();
list.Add(data1);
list.Add(data2);
list.Add(data3);
Is it possible?
In general, you'd have to either use a List<object> or create a non-generic base class, e.g.
public abstract class ValuePair
{
public string Name { get; set;}
public abstract object RawValue { get; }
}
public class ValuePair<T> : ValuePair
{
public T Value { get; set; }
public object RawValue { get { return Value; } }
}
Then you can have a List<ValuePair>.
Now, there is one exception to this: covariant/contravariant types in C# 4. For example, you can write:
var streamSequenceList = new List<IEnumerable<Stream>>();
IEnumerable<MemoryStream> memoryStreams = null; // For simplicity
IEnumerable<NetworkStream> networkStreams = null; // For simplicity
IEnumerable<Stream> streams = null; // For simplicity
streamSequenceList.Add(memoryStreams);
streamSequenceList.Add(networkStreams);
streamSequenceList.Add(streams);
This isn't applicable in your case because:
You're using a generic class, not an interface
You couldn't change it into a generic covariant interface because you've got T going "in" and "out" of the API
You're using value types as type arguments, and those don't work with generic variable (so an IEnumerable<int> isn't an IEnumerable<object>)
Not unless you have a non-generic base-type ValuePair with ValuePair<T> : ValuePair (it would work for an interface too), or use List<object>. Actually, though, this works reasonably:
public abstract class ValuePair
{
public string Name { get; set; }
public object Value
{
get { return GetValue(); }
set { SetValue(value); }
}
protected abstract object GetValue();
protected abstract void SetValue(object value);
}
public class ValuePair<T> : ValuePair
{
protected override object GetValue() { return Value; }
protected override void SetValue(object value) { Value = (T)value; }
public new T Value { get; set; }
}
No, it is not possible. You could create, in your case, a base class ValuePair from which ValuePair<T> derives. Depends on your purposes.
it's not possible as far as I know.
the line:
List<ValuePair> list = new List<ValuePair>();
you wrote in your sample is not providing a concrete type for T and this is the issue, once you pass it, you can only add object of that specific type.