Generic multiple submissions

I am trying to ignore the implementation of the interface by providing a factory / builder using generics. However, I run into a problem with multiple dispatchers and C # generators at runtime, which does something strange.

Main scenario: I defined several interfaces:

public interface IAddressModel { } public interface IUserModel { } 

Then I have a factory class to return the actual implementations:

 public class Factory { public T BuildModel<T>() { return BuildModel(default(T)); } public object BuildModel(object obj) { //this is here because the compiler will complain about casting T to //the first inteface parameter in the first defined BuildModel method return null; } public IAddressModel BuildModel(IAddressModel iModel) { //where AddressModel inherits from IAddressModel return new AddressModel(); } public IUserModel BuildModel(IUserModel iModel) { //where UserModel inherits from IUserModel return new UserModel(); } } 

The problem is that the factory is invoked as follows: new Factory().BuildModel<IAddressModel>() The BuildModel (...) method, which is dispatched at runtime from generics, is always the least derived form of T, in which case it is always object.

However, if you call new Factory().BuildModel(default(IAddressModel)); , the correct method is being superseded (most likely because it is done at compile time). It seems that dynamic dispatch with generics does not test methods for the derived type itself, even if the method called should be the same, regardless of whether it was executed at compile time or at run time. Ideally, I would like to make the BuildModel (...) methods private and only expose the general method. Is there any other way to get dynamic shift to call the correct method at runtime? I tried changing the implementation of BuildModel<>() to return BuildModel((dynamic)default(T)) , but this raises a run-time error due to the inability to determine which method to send. Is there a way to do this with contravariance and more interfaces?

+2
source share
2 answers

Perhaps you yourself can send the newsletter based on the type of argument T :

 public class Factory { private Dictionary<Type, Func<object>> builders = new Dictionary<Type, Func<object>> { { typeof(IAddressModel), BuildAddressModel }, { typeof(IUserModel), BuildUserModel } }; public T Build<T>() { Func<object> buildFunc; if (builders.TryGetValue(typeof(T), out buildFunc)) { return (T)buildFunc(); } else throw new ArgumentException("No builder for type " + typeof(T).Name); } private static IAddressModel BuildAddressModel() { return new AddressModel(); } private static IUserModel BuildUserModel() { return new UserModel(); } } 
+2
source

In the current state of the code, an explicit throw compilation is required.

 public T BuildModel<T>() { return (T)BuildModel(default(T)); } 

BuildModel treats T polymorphically as an object. BuildModel does not know that T is an IAddressModel or IUserModel unless you define such a restriction:

 public T BuildModel<T>() where T: IAddressModel { Console.WriteLine(typeof(T)); return (T)BuildModel(default(T)); } 

Now the compiler has enough information to find out that T is an IAddressModel . But for you, object needs to become a more derived parameter (covariant), which is not type safe. In other words, C # does not support covariance parameter types because it is not type safe.

You can still achieve factory as behavior using conditional logic:

  public T BuildModel<T>() { T result = default(T); if (typeof(T) == typeof(IAddressModel)) result = (T)BuildModel((IAddressModel)result); else if (typeof(T) == typeof(IUserModel)) result = (T)BuildModel((IUserModel)result); return result; } 
+1
source

All Articles