Generic Factory with type parameter - c#

I have the following situation.
My Factory class needs to create appropriate Strategy objects based on the input string argument to the CreateStrategy function.
Strategy1, Strategy2 etc are all derived from a common StrategyBase class. However each strategy has a different Validation mechanism which is the type parameter to the Factory class. However, the StrategyValidators are not of any common type and have different interfaces.
Therefore, in the below code, I am unable to specify any common constraint on the StrategyValidator type.
I am new to C# and hence not sure if there exists any mechanism to get over this design issue. Please suggest
public class Factory
{
//Create the appropriate Concrete Implementation class based on the type
public static StrategyBase CreateStrategy<StrategyValidator>(String Type)
{
StrategyBase EnumImp = null;
// WMI based implementation
if (Type == "Type1")
{
s = Strategy1<StrategyValidator>.Instance;
}
else if (Type = "Type2")
{
s = Strategy2<StrategyValidator>.Instance;
}
return s;
}
private StrategyBase s;
}
Here's the intended usage
Factory f = new Factory();
f.CreateStrategy<WMIValidator>("WMI");
f.CreateStrategy<ABCDValidator>("ABCD");
where WMIValidator and ABCDValidator are unrelated types, but the actual classes created by CreateStrategy function are related in a hierarchy e.g. having a common base StrategyBase
Here is a sample code to illustrate the issue
namespace TestCSharp
{
public interface IStrategy
{
};
public interface S1 : IStrategy
{
void f1();
void f2();
};
public class S1Concrete : S1
{
public void f1() { }
public void f2() { }
}
public interface S2 : IStrategy
{
void f3();
void f4();
};
public class S2Concrete : S2
{
public void f3() { }
public void f4() { }
};
public interface ProductBase
{
};
class Product1<T> : ProductBase where T : S1
{
};
class Product2<T> : ProductBase where T : S2
{
};
public class Factory
{
public ProductBase Create<T>(String Type)
{
if (Type == "P1")
return new Product1<T>();
else if (Type == "P2")
return new Product2<T>();
}
};
class Program
{
static void Main(string[] args)
{
Factory f = new Factory();
ProductBase s = f.Create<S1Concrete>("Type1");
}
}
}
The error I get is
The type 'T' cannot be used as type parameter 'T' in the generic type
or method 'TestCSharp.Product1'. There is no boxing conversion or
type parameter conversion from 'T' to 'TestCSharp.S1'.

I don't really understand your scenario fully but as far as I can tell the factory pattern you're using would have to instantiate products using reflection. This is a little ugly because it doesn't give the consumer any hints about what strategy types can be used with a given product name.
public class Factory
{
public ProductBase Create<T>(string name)
{
Type type;
switch (name)
{
case "P1":
type = typeof (Product1<>);
break;
case "P2":
type = typeof (Product2<>);
break;
case "P3":
type = typeof (Product3<>);
break;
default:
return null;
}
type = type.MakeGenericType(typeof (T));
return (ProductBase) Activator.CreateInstance(type);
}
}

I think that the answer in this case is, it depends on what you want Product and Strategy to do. What you seem to be trying to do is splitting your logic in two branches. Then you want to couple it again by using generics, but as you can notice, it won't work.
Consider a scenario, similar to yours above -- But where each class implementing IStrategy has one instead of two methods which does side effect (i.e. print a string). You use generics when the range of types allowed have something in common. In the case I just mentioned, both have a method returning void and accepting no parameters; so we can add a method to IStrategy, for instance:
public interface IStrategy
{
void ExecuteLogic();
};
public class S1 : IStrategy
{
public void ExecuteLogic()
{
OneMethod();
}
void OneMethod()
{
Console.WriteLine("Hello");
}
};
public class S2 : IStrategy
{
public void ExecuteLogic()
{
TotallyDifferentMethod();
}
void TotallyDifferentMethod()
{
Console.WriteLine("World");
}
};
Now, you also said that Strategy1 and Strategy2 have a different validation mechanism. However, it seems to me that you use them in the same method and context (and thus the same parameters and variables), so there must be something that makes them similar. Still, having defined IStrategy in the way we require, we can just use that as a constraint for Create<T>. So, Factory becomes:
public class Factory
{
public ProductBase Create<T>(String Type) where T : IStrategy
{
if (Type == "P1")
return new Product1<T>();
else if (Type == "P2")
return new Product2<T>();
return null;
}
};
But there's still one case. If you don't want Product1 to be called with S2 as a generic type, or Product2 to have S1 as its generic, then why using generics in the first place? You could easily couple the products with their relative strategies and also simplify the code remarkably.
In case I missed something (or the entire question) please leave a comment and I'll try to adapt my answer.
EDIT: since now you've redefined your example and used S1 and S2 as interfaces, I can see what you mean. A way would be defining multiple generic types and constraints for Factory.Create. Example:
public ProductBase Create<T1, T2>(String Type) where T1 : S1 where T2 : S2
It would be impossible otherwise, as you properly stated, because there's no common ancestor of S1 and S2 which can be accepted by your Product classes.

You can change the function to take StrategyValidator as type.
From
public static StrategyBase CreateStrategy<StrategyValidator>(String Type)
To
public static StrategyBase CreateStrategy<T>(String Type) where T:StrategyValidator
To answer you question, You cannot avoid conditional checks.
To simplify the code can move the different combinations ("Type1", "Type2" , etc) to either dictionary or to the configuration if you use Dependency Injection, and then can you reflection.
Example.
if (!dict.ContainsKey(key))
throw New InvalidArgumentException();
StrategyBase EnumImp = null;
var instance = dict[key].MakeGenericType(typeOf(type)).GetProperty("Instance", BindingFlags.Static | BindingFlags.Public )); //dict is Dictionary<string, Type>

Have you considered overloading the Create<> function? I don't have VisualStudio handy right now, but would the following code work for your situation?
namespace ... {
// ... other code here...
public class Factory {
public Product1<T> Create<T>() where T : S1 {
return new Product1<T>();
}
public Product2<T> Create<T>() where T : S2 {
return new Product2<T>();
}
}
class Program {
static void Main(string[] args) {
Factory f = new Factory();
ProductBase s = f.Create<S1Concrete>();
}
}
}
Additionally, you may wish to move your type constraints to a lower level. Consider writing an abstract base ProductBase class (that inherits from an IProductBase interface?) as follows:
class ProductBase<T> : IProductBase where T : IStrategy { }
This may help to alleviate some of your headaches.

Related

How to call generic method of derived class from base class reference in C#

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
{
}

Calling a generic method with interface instances

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...
}
}

Passing Enums as parameter from abstract class

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;
}
//...
}

Factory class returning a generic interface

I have few concrete which uses the following type of interface
interface IActivity<T>
{
bool Process(T inputInfo);
}
Concrete classes are like as follows
class ReportActivityManager :IActivity<DataTable>
{
public bool Process(DataTable inputInfo)
{
// Some coding here
}
}
class AnalyzerActivityManager :IActivity<string[]>
{
public bool Process(string[] inputInfo)
{
// Some coding here
}
}
Now how can i write the factory class which retuns a generic interface some thing like IActivity.
class Factory
{
public IActivity<T> Get(string module)
{
// ... How can i code here
}
}
Thanks
You should create generic method, otherwise compiler will not know type of T in return value. When you will have T you will be able to create activity based on type of T:
class Factory
{
public IActivity<T> GetActivity<T>()
{
Type type = typeof(T);
if (type == typeof(DataTable))
return (IActivity<T>)new ReportActivityManager();
// etc
}
}
Usage:
IActivity<DataTable> activity = factory.GetActivity<DataTable>();
Often this is implemented as in lazyberezovsky's answer. In c++ you could use template specialization to get compiler errors when you try to create a type the factory does not handle.
You can't do that in C# but you can get close. Though the code might look a little surprising which in turn could be a problem.
public static class Factory {
public static IActivity<someType> Get(this someType self){
//stuff specific to someType
}
public static IActivity<someOtherType> Get(someOtherType self){
//stuff specific to someOtherType
}
public static T Creator<T>(){
return null;
}
}
The usage would then be
IActivity<someType> act = Factory.Creator<someType>().Get();
of course this only works if you can pass a concrete type. If you need to pass a type parameter things get more complicated.

Generic contraints on method overloads

I've got an interface with some generic methods, and I wanted to implement a method with overloads to either accept an instance of a class, or its PK value (which is either an int or GUID but does vary).
I added to methods similar to these examples:
void DoSomething<TKey>(TKey key) where TKey: struct;
void DoSomething<TModel>(TModel model) where TModel : class;
The 'DoSomething' method name on the second of these is highlighted, and the error is
Type 'ISomeStuff' already defines a member called 'DoSomething' with
the same parameter types.
I'm surprised by this as I've clearly defined by parameters to be of different type: one is a class and the other a struct.
Why isn't this sufficient to make the signatures different?
Is possible to do it, you need create something like enable_if from C++
public class ClassTag<V> where V : class { }
public class StructTag<V> where V : struct { }
public void Func<V>(V v, ClassTag<V> dummy = null) where V : class
{
Console.Writeln("class");
}
public void Func<V>(V v, StructTag<V> dummy = null) where V : struct
{
Console.Writeln("struct");
}
public void Func<V>(V? v, StructTag<V> dummy = null) where V : struct
{
Console.Writeln("struct?");
}
static void Main()
{
Func("A");
Func(5);
Func((int?)5);
}
It can be expanded to use any disjoint where to distinguish between overloads.
Only drawback is that It cant be used inside another generic method:
public static void Z1<T>(T t) // where T : class
{
Func(t); //error there
}
public static void Z2<T>(T t) where T : class
{
Func(t); //ok
}
edit
But there is possibility of use dynamic in that case to work around this limitation:
public static void Z1<T>(T t)
{
Func((dynamic)t); //if `T == int` it will call "struct" version
}
Only drawback is run time cost similar to call to Dictionary<,> index.
Jon Skeet has an answer to everything: click me
quote:
the declarations only differ in generic constraints, and constraints
aren't part of the signature
If one wishes to invoke a member generically regardless of whether it has a class constraint or a struct constraint, and have it invoke a method with a suitable constraint, one may define an interface IThingUser<T> to act upon any type T, along with one a class which implements it for value types and another which implements it for class types. Have a static class ThingUsers<T> with a static field TheUser of type IThingUser<T>, and have it populate that field with an instance of one of the above classes, and then ThingUsers<T>.theUser will be able to act upon any sort of T.
public static class GenTest93
{
public interface IThingUser<T> { void ActOnThing(T it); }
class StructUser<T> : IThingUser<T>, IThingUser<Nullable<T>> where T : struct
{
void IThingUser<T>.ActOnThing(T it) { System.Diagnostics.Debug.Print("Struct {0}", typeof(T)); }
void IThingUser<Nullable<T>>.ActOnThing(T? it) { System.Diagnostics.Debug.Print("Struct? {0}", typeof(T)); }
}
class ClassUser<T> : IThingUser<T> where T : class
{
void IThingUser<T>.ActOnThing(T it) { System.Diagnostics.Debug.Print("Class {0}", typeof(T)); }
}
static class ThingUsers<T>
{
class DefaultUser : IThingUser<T>
{
public void ActOnThing(T it)
{
Type t = typeof(T);
if (t.IsClass)
t = typeof(ClassUser<>).MakeGenericType(typeof(T));
else
{
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
t = t.GetGenericArguments()[0];
t = typeof(StructUser<>).MakeGenericType(t);
}
TheUser = (IThingUser<T>)Activator.CreateInstance(t);
TheUser.ActOnThing(it);
}
}
static IThingUser<T> TheUser = new DefaultUser();
public static void ActOnThing(T it) {TheUser.ActOnThing(it);}
}
public static void ActOnThing<T>(T it) { ThingUsers<T>.ActOnThing(it); }
public static void Test()
{
int? foo = 3;
ActOnThing(foo);
ActOnThing(5);
ActOnThing("George");
}
}
It's necessary to use Reflection to create an instance of StructUser<T> or ClassUser<T> if the compiler doesn't know that T satisfies the necessary constraint, but it's not too hard. After the first time ActOnThing<T>() is used for a particular T, ThingUsers<T>.TheUser will be set to an instance which can be used directly for any future calls toActOnThing(), so performance should be very good.
Note that if given a Nullable<T>, the method creates a StructUser<T> and casts it to IThingUser<Nullable<T>>, rather than trying to create a sometype<Nullable<T>>, since nullable types themselves don't satisfy any constraint.
If you don't need generic parameters and just want to differentiate between these cases at compile time, you can use following code.
void Foo(object a) { } // reference type
void Foo<T>(T? a) where T : struct { } // nullable
void Foo(ValueType a) { } // value type

Categories