So, I have the following structure:
public abstract class MyBase
{
public Type TargetType { get; protected set; }
}
public class A : MyBase
{
public A()
{
TargetType = GetType();//Wrong, I need B class type not C
}
}
public class B : A
{
public B() { }
}
public class C : B
{
public C() { }
}
Of course, I can receive my type in this way:
public class B : A
{
public B()
{
TargetType = typeof(B);
}
}
Actually, I have to write some code to make the example clearer:
Class1.cs
public static Dictionary<Type, Type> MyTypes = new Dictionary<Type, Type>()
{
{ typeof(B),typeof(BView) }
}
public Class1()
{
C itemC = new C();
Class2.Initialize(itemC);
}
Class2.cs
public static Initialize(MyBase myBase)
{
Type t;
Class1.MyTypes.TryGetValue(myBase.TargetType, out t);
//I need get BView but I get null because *myBase.TargetType* is C class type
}
Level structure:
Level 0:(MyBase) - 1 object
Level 1:(A) - 2 objects
Level 2:(B) - 100 objects and more
Level 3:(C) - 80 objects and more
I gave this case in brackets
I will be grateful for any help
On any instance of an object you can call .GetType() to get the type of that object.
You don't need to set the type on construction
I didn't understand completely your question, but these are some possibilities to get informations about a type:
var a = new A();
Console.WriteLine(a.GetType().Name); // Output: A
Console.WriteLine(a.GetType().BaseType?.Name); // Output: MyBase
var b = new B();
Console.WriteLine(b.GetType().Name); // Output: B
Console.WriteLine(b.GetType().BaseType?.Name); // Output: A
// A simple loop to get to visit the derivance chain
var currentType = b.GetType();
while (currentType != typeof(object))
{
Console.WriteLine(currentType.Name);
currentType = currentType.BaseType;
}
// Output: B A MyBase
Also, I suggest to read this post about the difference between GetType and typeof
Hope this helps.
Related
I have 2 class which has a different constraint, and I want to create obj for them conditionally in a generic function. Example below.
public class Foo1<T>
where T : class, Interface1, new()
{
// do sth...
}
public class Foo2<T>
where T : class, Interface2, new()
{
//do sth...
}
public static void Create<T>()
{
if(typeof(Interface1).IsAssignableFrom(typeof(T))
{
var obj = new Foo1();
//...
} else if (typeof(Interface2).IsAssignableFrom(typeof(T))
{
var obj = new Foo1();
//...
}
}
And I got the error "There is no implicit reference conversion from T to Interface1/2".
The problem is similar to Similiar to How to conditionally invoke a generic method with constraints?, but I can find a place to add (dynamic).
You can create an instance of a generic class using reflection.
public static void Create<T>()
{
if (typeof(Interface1).IsAssignableFrom(typeof(T)))
{
var d1 = typeof(Foo1<>);
Type[] typeArgs = { typeof(T) };
var makeme = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(makeme);
}
else if (typeof(Interface2).IsAssignableFrom(typeof(T))
{
// same for Foo2
}
}
I created a class that inherits from KeyedByTypeCollection and extends it.
https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.keyedbytypecollection-1?view=netframework-4.7.2
KeyedByTypeCollection only has a Find method which returns null if no item is found. I prefer a TryGetValue method so I added one.
internal class TypeCollection<V> : KeyedByTypeCollection<V>
{
public T ValueOrDefault<T>() where T : V
{
if (!Contains(typeof(T)))
{
return default(T);
}
return (T)this[typeof(T)];
}
public bool TryGetValue<T>(out T value) where T : V
{
if (!Contains(typeof(T)))
{
value = default(T);
return false;
}
value = (T)this[typeof(T)];
return true;
}
}
The thing is there is no reason for inheritance. I just want to extend an existing class. I started with these two extension methods
internal static class KeyedByTypeCollectionExtensions
{
public static T ValueOrDefault<T>(this KeyedByTypeCollection<V> collection) where T : V
{
if (!collection.Contains(typeof(T)))
{
return default(T);
}
return (T)collection[typeof(T)];
}
public static bool TryGetValue<T>(this KeyedByTypeCollection<V> collection, out T value) where T : V
{
if (!collection.Contains(typeof(T)))
{
value = default(T);
return false;
}
value = (T)collection[typeof(T)];
return true;
}
}
but how do I setup these extension methods? What do I have to set for the generic type V?
You will have to define V.
public static T ValueOrDefault<T,V>(this KeyedByTypeCollection<V> collection) where T : V
and
public static bool TryGetValue<T,V>(this KeyedByTypeCollection<V> collection, out T value)
where T : V
It will work nice with the TryGetValue, because the compiler will know which types are used, but for the ValueOrDefault you will have to set both of the types, which is a bit ugly.
Lets consider the following classes:
public class A { }
public class B : A { }
Then usage can be:
var myCollection = new KeyedByTypeCollection<A>();
myCollection.Add(new A());
myCollection.Add(new B());
myCollection.TryGetValue(out B b); // <-- Nice! :)
b = myCollection.ValueOrDefault<B,A>(); // <-- Ugly :(
Consider:
class BasicType
{
public BasicType() { }
public T Save<T>() where T : BasicType
{
BasicType b = DataContext.Save(this); //Returns a BasicType
return (T)Activator.CreateInstance(typeof(T), b);
}
}
class DerivedType : BasicType
{
public DerivedType(BasicType b) { }
}
public static void Main()
{
DerivedType d = new DerivedType();
d = d.Save<DerivedType>();
}
This works, but being forced to specify the type each time I call Save is a drag.
Is there some way to change the BasicType.Save method such that it will always return an instance of the actual type (derived or base) of the instance on which Save is being called?
Generics are not needed in this case.
I think this should be enough:
public BasicType Save()
{
BasicType b = DataContext.Save(this); //Returns a BasicType
return (BasicType)Activator.CreateInstance(this.GetType(), b);
}
Anyway you should be carefull with this as the inherited classes may not have the expected constructor.
Is better to override the save method, or at least the specific part.
You could change your definition of BasicType so that you're forced to provide the type of T at the point of inheritance.
Something like this:
class BasicType<T> where T : BasicType<T>, new()
{
public BasicType() { }
public T Save()
{
T b = new T();
return (T)Activator.CreateInstance(typeof(T), b);
}
}
class DerivedType : BasicType<DerivedType>
{
public DerivedType() { }
}
class Program
{
static void Main(string[] args)
{
DerivedType d = new DerivedType();
d = d.Save();
}
}
Something like this.
class BasicType
{
public BasicType()
{
}
protected virtual T Save<T>()
{
BasicType b = DataContext.Save(this); //Returns a BasicType
return (T)Activator.CreateInstance(typeof(T), b);
}
}
class DerivedType : BasicType
{
public DerivedType(BasicType b)
{
}
public DerivedType Save()
{
return base.Save<DerivedType>();
}
}
public static void Main()
{
DerivedType d = new DerivedType(new BasicType());
d = d.Save();
}
For example:
I have the following classes:
public class A {}
public class B:A {}
public class C:B {}
public class D:C {}
If there is a convenient method like to get a hierarchy distance to the base class(instead of testing D.IsSubclassOf(B)) to determine if D is closer to A, or B is closer to A?
I've taken the code snipped suggested by #kha and adapted it for you, from this answer: Get inheritance tree of type
public static class TypeExtensions
{
public static IEnumerable<Type> GetInheritancHierarchy
(this Type type)
{
for (var current = type; current != null; current = current.BaseType)
yield return current;
}
public static int GetInheritanceDistance<TOther>(this Type type)
{
return type.GetInheritancHierarchy().TakeWhile(t => t != typeof(TOther)).Count();
}
}
Usage
var c = new C(); // 2
Console.WriteLine(c.GetType().GetInheritanceDistance<A>());
var b = new B(); // 1
Console.WriteLine(b.GetType().GetInheritanceDistance<A>());
Maybe this is of any help:
public static int? GetDegreeOfRelationship(Type typeA, Type typeB)
{
if (typeA.IsInterface || typeB.IsInterface) return null; // interfaces are not part of the inheritance tree
if (typeA == typeB) return 0;
int distance = 0;
Type child;
if (typeA.IsAssignableFrom(typeB))
{
child = typeB;
while ((child = child.BaseType) != typeA)
distance--;
return --distance;
}
else if(typeB.IsAssignableFrom(typeA))
{
child = typeA;
while ((child = child.BaseType) != typeB)
distance++;
return ++distance;
}
else
return null;
}
Usage:
int? distance = GetDegreeOfRelationship(typeof(A), typeof(D)); // -3
int? distance = GetDegreeOfRelationship(typeof(D), typeof(A)); // 3
int? distance = GetDegreeOfRelationship(typeof(B), typeof(B)); // 0
int? distance = GetDegreeOfRelationship(typeof(D), typeof(string)); // null
You can use extension methods to extend the behaviour of Type to iterate through the hierarchy to come up with distance to base or the path to base:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
System.Windows.Forms.Form a = new System.Windows.Forms.Form();
Console.WriteLine(a.GetType().DistanceToBase());
Console.WriteLine(a.GetType().PathToBase());
}
}
public static class Extensions
{
public static int DistanceToBase(this Type ob)
{
if (ob.BaseType == null)
{
return 0;
}
else return 1 + ob.BaseType.DistanceToBase();
}
public static string PathToBase(this Type ob)
{
if (ob.BaseType == null)
{
return ob.Name;
}
return ob.Name + "->" + ob.BaseType.PathToBase();
}
}
How can I set generic type dynamically?
public class A
{
public int X { get; set; }
public A()
{
X = 9000;
}
}
public class Class1
{
public void Test()
{
List<A> theList = new List<A>() {
new A { X = 1 },
new A { X = 2 }
};
object testObj = theList;
var argType = testObj.GetType().GetGenericArguments()[0];
Foo(testObj as ICollection<argType>); // ?
}
public void Foo<T>(ICollection<T> items) where T:new()
{
T newItem = new T();
items.Add(newItem);
}
To do in "regular" c# you would use reflection to obtain the MethodInfo, then use MakeGenericMethod() and Invoke(). However, this is easier:
Foo((dynamic)testObj);
The reflection approach here is:
var method = typeof(Class1).GetMethod("Foo").MakeGenericMethod(argType);
method.Invoke(this, new object[] { testObj });
You can't do that, because in the Foo function you are supposed to do something with the collection, and there's no guarantee that the type will be safe.
The only way is using an "object" then casting to the proper type within the Fooo function.