Friday, April 13, 2018

Using a type to instantiate a derived class which uses restricted generics

Leave a Comment

I'm using a generic factory class where the generic part is the derived class that is being used. The normal usage is clear: BaseClass<DerivedA> C = new BaseClass<DerivedA>(). Now though I'm trying to put in property injection into the class where I use these classes. To do that I tried to give Type as a Parameter (so that I can inject which derived class is being used).

Now though I'm a bit at a loss despite looking for examples and trying around myself. And I'm now wondering: Is such a construct possible at all to use? And if so how can I instantiate the class and use Exists and ExistsB?

Usage:

public class MyMainClass {     object _ClassInstance; // BaseClass<DerivedA> or BaseClass<DerivedB>      public MyyMainClass(Type typeIWant)     {           .....     } }  .... MyMainClass a = new MyMainClass(typeof(DerivedA)); MyMainClass b = new MyMainClass(typeof(DerivedB)); 

Generic class:

public abstract class BaseClass<T> where T: BaseClass<T>, new() { ...     public bool Exists(int a) {...} } 

Derived class:

public class DerivedA :BaseClass<DerivedA> { ... }  public class DerivedB :BaseClass<DerivedB> { ...    public bool ExistsB(string a) {...} } 

4 Answers

Answers 1

You can create an instance of any type you pass with this:

_ClassInstance = Activator.CreateInstance(typeIWant) 

It isn't recommended though, because you will be forever checking its type and casting it when you want to use one of the BaseClass methods. If you are able to change your main class to take a type parameter, it should look like this:

public class MyMainClass<T> where T: BaseClass<T>, new() {     T _ClassInstance; // BaseClass<DerivedA> or BaseClass<DerivedB>      public MyMainClass()     {         _ClassInstance = new T();     } } 

This will allow you to use any methods defined on BaseClass. Given you want to use ExistsB, which isn't, you will still need to check its type at run-time and cast it. I would recommend something like this:

if (_ClassInstance is DerivedB derivedB)     derivedB.ExistsB("..."); 

If you really need compile-time checking, and I would recommend it, your classes will need to change. You can't access a method or property defined on a derived class with a reference to the base class only.

Answers 2

Is this what you want?

Object instance1 = Activator.CreateInstance<Object>(); Object instance2 = Activator.CreateInstance(typeof(object)); 

Answers 3

I recommend you to use getType to take a type name. In case of you level up the inheritance (inherit another class from DerivedB etc), If you use getType you can be sure that you call this method for only for the instances of DerivedB types.

MyMainClass b = new MyMainClass(typeof(DerivedB));  if(b.GetType() == typeof(DerivedB))  b.ExistsB(...); 

Answers 4

This is main problem of abstract classes - you share interface AND implementation. But in your case, you strongly depend on different interfaces. To simplify problem, just look at what you actually want:

public interface IExistable<in TValue> {     bool Exists(TValue value); }  public interface IDerivedA : IExistable<int> { }  public interface IDerivedB : IExistable<int>, IExistable<string> { } 

As you can see, you overcomplicating IDerivedB with two interfaces, that said, your MainClass depends on IDerivedB on some occasions. It is not bad to extend interface, you just using it wrong. So what you must do, is define interface for your MainClass:

public interface IMainClass {     //some methods here } 

And create two implementations:

public class ConcreteMainClass : CommonMainClass //you can derive from common, delegate to inner instance or implement interface from scratch - you decide depending on situation {     private readonly IDerivedB _instance;     public ConcreteMainClass(IDerivedB instance) : base(instance)     {         _instance = instance;     }      //you can override some logic here depending on IExistable<string> from IDerivedB }  public class CommonMainClass : IMainClass {     private readonly IExistable<int> _instance;     public CommonMainClass(IExistable<int> instance)     {         _instance = instance;     }      //this is where you don't depend on IExistable<string> } 

This way you can write more clear code:

IMainClass a = new CommonMainClass(new DerivedA()); IMainClass b = new ConcreteMainClass(new DerivedB()); 

PS: It is better to pass dependencies to upper level of class life cycle, instead of activating it inside, so you can use factory which will create them for you or even Dependency Injection.

PS2: Use interfaces before abstract classes. Interfaces/Constructors/Methods describes your overall dependencies. Abstract classes on the other hand is just a way to reuse implementation - they are not meant to define interface or how you work with class, you can make entire body private/protected without sharing anything out.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment