I want to do something at runtime with objects based on there type.
Depending on the type I need to do different operations so I thought I would create
an abstract class like this:
internal abstract class DataOperator<T>
where T : class
{
public abstract void DoSomething(IList<T> data);
}
with multiple concrete implementations like:
class MyClassOperator : DataOperator<MyClass>
{
public override void DoSomething(IList<MyClass> data)
{
throw new NotImplementedException();
}
}
But how would I actually create an instance of the concrete class at runtime?
The problem is that DataOperator<A> and DataOperator<B> are not assignment compatible. Not even if A and B are. So, you cannot assign your classes to, say DataOperator<object>. Therefore, you will have to treat them individually as non related types.
A way out is to have a non generic base type or interface.
interface IDataOperator
{
public void DoSomething(IList data);
}
We create an abstract class that implements it explicitly in order to hide this weakly typed DoSomething when not called through the interface. We also introduce the generic type parameter.
abstract class DataOperator<T> : IDataOperator
where T : class
{
void IDataOperator.DoSomething(IList data)
{
if (data is IList<T> listT) {
DoSomething(listT);
} else {
throw new ArgumentException("The List is not compatible.", nameof(data));
}
}
abstract public void DoSomething(IList<T> data);
}
We implement concrete types like this:
class DataOperatorA : DataOperator<MyClassA>
{
public override void DoSomething(IList<MyClassA> data)
{
throw new NotImplementedException();
}
}
class DataOperatorB : DataOperator<MyClassB>
{
public override void DoSomething(IList<MyClassB> data)
{
throw new NotImplementedException();
}
}
These implementations are assignment compatible to our interface. Example:
IDataOperator[] operators = { new DataOperatorA(), new DataOperatorB() };
If you want to create different operators depending on other data, you can create a factory class. In this example, we use a string as discriminator, but it could be anything else, like a System.Type or an enum, etc.
static class DataOperator
{
public static IDataOperator Create(string op)
{
return op switch {
"A" => new DataOperatorA(),
"B" => new DataOperatorB(),
_ => throw new ArgumentException("Unknown operator.", "op")
};
}
}
Same example as above but with the factory:
IDataOperator[] operators = { DataOperator.Create("A"), DataOperator.Create("B") };
I resolved this issue using a factory as suggested by Matthew Watson.
DataOperator is actually AggregateRootMigrator
public static class AggregateRootMigratorFactoryProvider
{
public static AggregateRootMigrator<T> GetAggregateRootMigrator<T>()
where T : AggregateRoot
{
var type = typeof(AggregateRootMigrator<T>).Assembly.GetTypes().Where(
t =>
{
if (t.IsAbstract == false && t.IsSubclassOf(typeof(AggregateRootMigrator<T>)))
{
return true;
}
return false;
}).SingleOrDefault();
if (type == null)
{
throw new InvalidOperationException($"No Factory found for eventType: {typeof(T).Name}");
}
return (AggregateRootMigrator<T>)Activator.CreateInstance(type);
}
}
now I can use it like this:
var dataMigrator = AggregateRootMigratorFactoryProvider.GetAggregateRootMigrator();
var result = dataMigrator.Migrate(data);
Related
I have the following base class (omitted version):
class BaseClass
{
}
I create the following derived classes:
class DataPreparationClass<T> : BaseClass
{
}
class DataClass<T,U> : DataPreparationClass<T>
{
public virtual void Start<U>(U arg)
{}
}
class DataClassMain : DataClass<InputData,Data>
{
public override void Start(Data argument)
{
base.Start(argument);
}
}
class DataClassAux : DataClass<InputData,AuxData>
{
public override void Start(AuxData argument)
{
base.Start(argument);
}
}
I have a List<BaseClass> containing various derived instances (there are more derived types) and I intend to call their Start method respectively:
List<BaseClass> instances = GetAllInstance();
foreach(BaseClass instance in instances)
{
object arg = GetArgsForInstance(instance);
// instance.Start(arg); //can't call this
}
However, as their common base is the BaseClass, I can't call Start without casting to...basicly every possible type as their types are unknown at the processing.
If I use dynamic:
((dynamic)target).Start(new Data("starting")); //target is of type DataClassMain<InputData,Data>
I get an exception:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'The best
overloaded method match for
'Client.DataClass<InputData,Data>.Start(Data)' has some invalid
arguments'
So how should I call the unknown method?
So, the most straight forward answer to your question would be to use pattern matching to call the start method.
List<BaseClass> instances = GetAllInstance();
foreach(BaseClass instance in instances)
{
object arg = GetArgsForInstance(instance);
switch(instance){
case DataClassMain d : d.Start((Data)arg); break;
case DataClassAux a : a.Start((AuxData)arg);break;
default: throw new Exception();
}
}
But I do get the feeling this is an convoluted and inappropriate inheritance chain, and you should really consider using a factory and/or strategy pattern instead.
It's assumed that GetArgsForInstance allways will return the correct type with respect to the type it receives as an argument, and that the return types (Data, AuxData and so on) share a common base type. So we could do the type resolution directly with a signature of T GetArgsForInstance<T>(BaseClass b). That way you can make sure you get args of the right type before you return it.
Since the Start overrides just pass along the call generic types, so the overrides in DataClassMain and DataClassAux are unnecessary.
If we modify DataClass a bit we can then do it like this:
class DataClass<T,U> : DataPreparationClass<T>
{
public virtual void Start(U arg)
{
//Do somethin with arg
}
public void Call(Func<BaseClass,U> f){
U data = f.Invoke(this);
Start(data);
}
}
and invoke it with
List<BaseClass> instances = GetAllInstance();
foreach(BaseClass instance in instances)
{
switch(instance)
{
case DataClassMain d : d.Call(GetArgsForInstance<Data>); break;
case DataClassAux a : a.Call(GetArgsForInstance<AuxData>);break;
default: throw new Exception();
}
}
The reason this is preferable is that we can let the compiler ensure that we only pass the appropriate types to the different methods, no casting needed.
But again, such a convoluted inheritance chain should almost always be avoided.
I would say, your questions shows multiple flaws in your model:
by definition of your classes, there is no polymorphism inbetween you Start() methods : Start(Data) do not override Start<U>(U)
by definition of your GetArgsForInstance() method, you have lost the type information you need.
I would add that Classes that are called Classes and Data that are called Data and that are parameterized with their content are way too generic.
That saying, your question implies that you are not wanting to fix those flaws, maybe they are out of your control, so you have to live with it :
instead of not loosing the Type information, you ask for a way to retrieve it.
instead of using polymorphism to retrieve the type from the best place to retrieve do so (in my opinion), which is the Start method itself, you ask for a way to retrieve it in the calling code.
So, what I would try to do is :
rework your GetArgsForInstance() method to be able not to loose this information, for instance, replace it by an object, something like :
class DataClassMain : DataClass<InputData,Data>
{
public override void Start(ArgumentProvider argumentProvider)
{
Data argument = argumentProvider.getArgumentAsData(argumentProvider);
base.Start(argument);
}
}
if not possible, retrieve the types from the inside of the derived classes, for instance something like :
public class DataClassMain : DataClass<InputData,Data>
{
public override void Start(object arg)
{
base.Start(arg);
Data argAsData = (Data) arg;
}
}
if not possible, that means you already have a set of constraint that is making your code hard to maintain, so let's go for a messy reflective thing, but you have to be aware that there is no polymorphism involved and get rid of your 'override' and 'virtual' modifier on Start() methods. Here is a fully working program, which output is :
DataClassMain
DataClassAux
public static void Main(string[] args)
{
List<BaseClass> instances = GetAllInstance();
foreach(BaseClass instance in instances)
{
object value = GetArgsForInstance(instance);
messyInvoke(instance, value);
}
}
private static void messyInvoke(BaseClass instance, object value)
{
MethodInfo method = instance.GetType().GetMethod("Start");
if (method != null)
{
ParameterInfo[] parametersInfos = method.GetParameters();
if (parametersInfos.Length == 1)
{
object[] paramArray = {value};
method.Invoke(instance, paramArray);
}
}
}
public class BaseClass{
public virtual Type GetTypeOfArgs()
{
return typeof(Toto);
}
}
public class DataPreparationClass<T> : BaseClass {}
public abstract class DataClass<T> : DataPreparationClass<T>
{
}
public class DataClassMain : DataClass<Toto>
{
public void Start(Data arg)
{
Console.WriteLine("DataClassMain");
}
}
public class DataClassAux : DataClass<Toto>
{
public void Start(AuxData argument)
{
Console.WriteLine("DataClassAux");
}
}
private static object GetArgsForInstance(BaseClass isntance)
{
if (isntance is DataClassMain)
return new Data();
if (isntance is DataClassAux)
return new AuxData();
throw new ArgumentException();
}
private static List<BaseClass> GetAllInstance()
{
return new List<BaseClass> {new DataClassMain(), new DataClassAux()};
}
public class Toto{}
public class DataClassInputData
{
}
public class Data : DataClassInputData
{
}
public class AuxData : DataClassInputData
{
}
I have next interface
public interface IProperty<T>
{
T Data { get; set; }
}
public abstract class SomeAbsProperty<T> : IProperty<T> where T : class
{
protected SomeAbsProperty(int param1) {}
public abstract T GetData();
public I Data { get; set; }
}
And I have list of childres classes that based on SomeAbsProperty class
they looks like (simple example)
public sealed class ChildrenProperties : SomeAbsProperty<SomeClasss>
{
public ChildrenProperties(int param1):base(param1) {}
public override object GetData()
{
return new SomeClasss()
}
}
I would like to have some factory that would build specific class based on some type
public static class MyFactory
{
public static SomeAbsProperty<T> CreateObject<T>(PropertyName property) where T : class
{
switch (property)
{
case PropertyName.p1:
return new ChildrenProperties1(siteSettings, packageDateContext);
case PropertyName.p2:
return new ChildrenProperties(siteSettings, packageDateContext);
case PropertyName.p3:
return new ChildrenProperties2(siteSettings, packageDateContext);
case PropertyName.p4:
return new ChildrenProperties3(siteSettings, packageDateContext);
default:
return null;
}
}
}
but compelator can't convert my clases to SomeAbsProperty
what would be correct behavior here ?
You can use as casting to SomeAbsProperty<T> generic class, something like
return new ChildrenProperties(10) as SomeAbsProperty<T>;
Of-course you must be sure that ChildrenProperties is indeed SomeAbsProperty (which you know it is if you wrote base classes and factory class). You can not use explicit compile time casting.
Edit:
Maybe its better if factory which creates instances only depends on generic parameter (this will work only if all specializations have different parameter T; I'm not sure if that is your situation). Something like:
public static SomeAbsProperty<T> CreateObject<T>() where T : class
{
Type type = typeof(T);
if (type == typeof(object))
{
return new ChildrenProperties() as SomeAbsProperty<T>;
}
else if (type == typeof(string))
{
return new ChildrenPropertiesString() as SomeAbsProperty<T>;
}
else
{
return null;
}
}
... then you can call factory with something like:
SomeAbsProperty<object> h = MyFactory.CreateObject<object>();
Console.WriteLine(h.GetType().ToString());
SomeAbsProperty<string> h2 = MyFactory.CreateObject<string>();
Console.WriteLine(h2.GetType().ToString());
As a followup question to this one
public interface IFeature { }
public class FeatureA : IFeature { }
IFeature a = new FeatureA();
Activate(a);
private static void Activate<TFeature>(TFeature featureDefinition) where TFeature : IFeature
{
}
I undestand, that once the FeatureA is casted to IFeature the generic method will always get IFeature as type parameter.
We have a service with provides us with a list features (List<IFeature>). If we want to iterate over those features, passing each in the generic method, I guess there is no way to get the concrete type in the generic method other than
using reflection
using a dynamic variable to determine the type on runtime (Calling a generic method with the correct derived type)
Since reflection is very costly, I would like to use the dynamic cast. Is there any downside to call the method that way? Somehow I feel dirty when doing that :-)
You can use visitor pattern as follows assuming that you can modify your codebase. Otherwise, use dynamic.
public interface IFeature
{
void Accept(Visitior visitor);
}
public class FeatureA : IFeature
{
public void Accept(Visitior visitor)
{
visitor.Visit(this);
}
}
public class FeatureB : IFeature
{
public void Accept(Visitior visitor)
{
visitor.Visit(this);
}
}
public class Visitior
{
public void Visit<TFeature>(TFeature feature) where TFeature : IFeature
{
Console.WriteLine(typeof(TFeature) == feature.GetType());//True
}
}
static void Main(string[] args)
{
List<IFeature> features = new List<IFeature>
{
new FeatureA(),
new FeatureB()
};
Visitior visitor = new Visitior();
foreach (var item in features)
{
item.Accept(visitor);
}
}
You can obtain the type object for generic/(not generic) type using typeof:
public static T Parse<T>(String value)
{
object result = default(T);
var typeT = typeof (T);
if (typeT == typeof(Guid))
{
result = new Guid(value);
}
else if (typeT == typeof(TimeSpan))
{
result = TimeSpan.Parse(value);
}
else
{
result = Convert.ChangeType(value, typeT);
}
return (T)result;
}
My simple method returns T. And this is a key point. It must be generic to allow developer to specify return type. If method doesn't return generic and only accepts one then there are several reasons to make it generic. To avoid box/unbox operations on method arguments or to tackle with situation when method takes argument of different types which are not inherited from common base class/interface. And it's not your case. So the method in your code haven't to be generic. Just type you argument as IFeature and use is/as/GetType():
private static void Activate(IFeature feature)
{
if (feature is FeatureImplementationA)
{
//Do something...
}
}
How to do things like this
List<Type:IMyInterface> a = new List<Type:IMyInterface>;
a.Add(typeof(MyClass1)); //MyClass1..3 implementing IMyInterface
a.Add(typeof(MyClass2));
a.Add(typeof(MyClass3));
IMyInterface c = default(a[1]); //create MyClass2 object
a.Add(typeof(Object)); //must fail
without constructing object first or checking type later?
what you want is not directly supported in C#. since Constraints on Type parameter can only be specefied on constructor, inheritance hierarchy, interface implementation and a few others. more details
you can do it in a different way, however in this approach there is no compile time error:
public interface IMyConstraint
{
void Do();
}
public class MyClass: IMyConstraint
{
public void Do()
{
}
}
// Inherit from the List class to add some functionality to it
public class MyTypeList<T> : List<T> where T : System.Type
{
public MyTypeList()
{
}
// use new keyword to prevent client from using the List.Add method.
public new void Add(T type)
{
// here you check if the type is implementing the interface or not
if (!typeof(IMyConstraint).IsAssignableFrom(type))
{
// if it dose not implement the interface just throw an exception
throw new InvalidOperationException();
}
// call the original List.Add method
base.Add(type);
}
}
You can do this if you know the types involved statically:
public class TypeList<T>
{
private readonly List<Type> types = new List<Type>();
public void Add<D>() where D : T, new()
{
this.types.Add(typeof(D));
}
public T NewAt(int index)
{
return (T)Activator.CreateInstance(this.types[index]);
}
}
then you can do:
var a = new TypeList<IMyInterface>;
a.Add<MyClass1>();
a.Add<MyClass2>();
a.Add<MyClass3>();
IMyInterface c = a.NewAt(1);
a.Add<object>(); //won't compile
I have an abstract base class, TestFactory which looks like:
public abstract class TestFactory
{
//static method that can create concrete factories
public static TestFactory CreateTestFactory(FactoryType factoryType)
{
TestFactory factory = null;
switch (factoryType)
{
case FactoryType.Blood:
factory = new BloodTestFactory();
break;
case FactoryType.Urine:
factory = new UrineTestFactory();
break;
default:
break;
}
return factory;
}
//BloodTestFactory and UrineTestFactory are concrete types
//that will need to create their own tests
//this enum parameter needs to 'switch' between
//BloodTestType and UrineTestType
public abstract LabTest CreateTest(Enum e);
}
public enum FactoryType
{
Blood,Urine
}
So this class creates a concrete factory like:
public class BloodTestFactory :TestFactory
{
//both BloodTestFactory and UrineTestFactory will create a LabTest object
//I would like to have a BloodTestType and UrineTestType enum, what do I need
//to do to pass a generic Enum as a parameter and then switch on BloodTestType
public override LabTest CreateTest(Enum e)
{
BloodTest bt = null;
//switch (e)
//{
// default:
// break;
//}
//creation logic here
}
}
public enum BloodTestType
{
H1AC,Glucose
}
A BloodTest itself is an abstract class which will return a concrete BloodTest object based on the Enum value. For clarity's sake I would like to have a BloodTestType and a UrineTestType (not shown) enum. Since the CreateTest method is abstract, how can I make sure that I can pass it a BloodTestType when I want to create BloodTests and a UrineTestType enum when I want to create UrineTest?
I'm not sure if this is the best approach to take for this requirement, I'll leave the design pattern discussion for comments / other answers.
I wouldn't necessarily do it this way, however, to make the code you have work, I would introduce a class level generic parameter on TestFactory for the enum.
public abstract class TestFactory<TTestType>
{
public abstract LabTest CreateTest(TTestType testType);
}
Derived classes then simply specify the generic argument:
public class BloodTestFactory : TestFactory<BloodTestType>
{
public override LabTest CreateTest(BloodTestType e)
{
}
}
Note that unless you use something like Unconstrained Melody you don't get much support for generic type constraints on enum.
Also note that this now makes it difficult to reference the shared base class because you need to close the generic argument:
TestFactory baseReference = myBloodTestFactory; // Not possible.
TestFactory<BloodTestType> baseReference = myBloodTestFactory;
Personally I'd likely decompose these two factories into separated classes without a base, or look into using an interface. Dealing with specific parameters in what you would like to have as "common" methods is difficult.
There is a nice way of doing this, and it results in code such as:
var client = new LabTestClient();
client.Run<BloodTestFactory,BloodTestType>(BloodTestType.H1AC);
client.Run<BloodTestFactory,BloodTestType>(BloodTestType.Glucose);
// outputs
// BloodTest: H1AC
// BloodTest: Glucose
Here is the rest of the code, hopefully its quite self explanatory how to use it/extend it to your needs - there are concrete classes for each type of lab test.
First the client and the abstract classes
public class LabTestClient
{
public void Run<TFactory, TInput>(TInput input) where TFactory : LabTestFactory, new()
{
LabTestFactory factory = new TFactory();;
LabTest labTest = factory.CreateLabTest();
labTest.Run(input);
}
}
public abstract class LabTest
{
public abstract void Run<TInput>(TInput input);
}
public abstract class LabTestFactory
{
public abstract LabTest CreateLabTest();
}
Then the concrete implementation:
public enum BloodTestType{ H1AC,Glucose }
public class BloodTest: LabTest
{
public override void Run<TInput>(TInput input)
{
Console.WriteLine("BloodTest: {0}",input);
}
}
public class BloodTestFactory : LabTestFactory
{
public override LabTest CreateLabTest()
{
return new BloodTest();
}
}
public enum UrineTestType{ A,B }
public class UrineTest: LabTest
{
public override void Run<TInput>(TInput input)
{
Console.WriteLine("UrineTest: {0}",input);
}
}
public class UrineTestFactory : LabTestFactory
{
public override LabTest CreateLabTest()
{
return new UrineTest();
}
}
A live example: http://rextester.com/MUV89315
However, this is still not ideal, as the individual tests still don't enforce their input - and in fact when you come to do anything complex within the test you'll find you don't know what the actual type of TInput is unless you cast it to the right enum:
public class UrineTest: LabTest
{
public override void Run<TInput>(TInput input)
{
var urineTestType = (UrineTestType)input;
// do something useful.
}
}
Just test for type safety in your overriden method:
public override LabTest CreateTest(Enum e)
{
BloodTest bt = null;
if(!e.GetType.Equals(typeof(BloodTestType)))
{
// throw type exception of your choice
}
}
Just add an argument check to your base class:
public abstract class TestFactory
{
//static method that can create concrete factories
public static TestFactory CreateTestFactory(FactoryType factoryType)
{
if (!Enum.IsDefined(typeof(FactoryType), factoryType)
{
throw InvalidEnumArgumentException(...);
}
TestFactory factory = null;
switch (factoryType)
{
case FactoryType.Blood:
factory = new BloodTestFactory();
break;
case FactoryType.Urine:
factory = new UrineTestFactory();
break;
default:
break;
}
return factory;
}
//...
}