Generic factory that creates generic classes

I have a set of value type converters that convert strings to their respective types. I have a factory that is responsible for creating these type-based converters when necessary. I am trying to keep the factory and converters common, but I am facing some problems. I do not know the type until I call the .Create method in the factory, so I need to pass this type as an argument. The problem is that then my .Create method thinks I'm looking for a ValueConverter<Type> instead of a more suitable value converter, like ValueConverter<int> . I am missing something, or maybe even mistaken.

Here are a few of my converters and the interface:

 public interface IValueConverter<T> { T Convert(object objectToConvert); } public class IntValueConverter : IValueConverter<int> { public int Convert(object objectToConvert) { return System.Convert.ToInt32(objectToConvert); } } public class DateTimeValueConverter : IValueConverter<DateTime> { public DateTime Convert(object objectToConvert) { return System.Convert.ToDateTime(objectToConvert); } } 

Then I have a factory like this:

 public class ValueConverterFactory : IValueConverterFactory { private readonly IUnityContainer _container; public ValueConverterFactory(IUnityContainer container) { _container = container; } public IValueConverter<T> Create<T>(T type) { return _container.Resolve<IValueConverter<T>>(); } } 

And unity is set up something like this:

 Container.RegisterType<IValueConverter<int>, IntValueConverter>(); Container.RegisterType<IValueConverter<DateTime>, DateTimeValueConverter>(); 

I need to be able to call factory as follows:

 var objectType = someObj.GetType(); var valueConverter = _valueConverterFactory.Create(objectType); 
+4
source share
5 answers

The problem is that then my .Create method thinks I'm looking for a ValueConverter<Type> instead of a more suitable value converter, like ValueConverter<int> .

First, you must understand why this is happening. You did not give us the calling code, but it probably looks something like this:

 Type type = SomehowResolveTheTypeThatINeedToConvertTo(); factory.Create(type); 

Right there, it will call a generic method

 IValueConverter<T> ValueConverterFactory.Create<T>(T type) 

where Type is replaced by the type parameter T

Secondly, you need to understand that what you are trying fundamentally cannot be done. You do not know the type at compile time, and therefore you cannot have strong typing. To return to the strongly typed IValueConverter<T> , you need to know what T . You either have to accept that your converters return object instead of T or find a way to make it so that you know the type of T at compile time.

+1
source

I donโ€™t know if this is what you are asking for, but I found this elegant and short code in the Rhino Igloo structure: ConversionUtil.cs - it will convert the string to any type ...

  public static object ConvertTo(Type type, string inject) { if (inject == null) { return type.IsValueType ? Activator.CreateInstance(type) : null; } if (type.IsInstanceOfType(inject)) { return inject; } else if (type == typeof(int)) { int temp; if (int.TryParse(inject, out temp)) return temp; return null; } else if (typeof(IConvertible).IsAssignableFrom(type)) { return Convert.ChangeType(inject, type); } //Maybe we have a constructor that accept the type? ConstructorInfo ctor = type.GetConstructor(new Type[] { inject.GetType() }); if (ctor != null) { return Activator.CreateInstance(type, inject); } //Maybe we have a Parse method ?? MethodInfo parseMethod = type.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public); if (parseMethod != null) { return parseMethod.Invoke(null, new object[] { inject }); } throw new ArgumentException(string.Format( "Cannot convert value '{0}' of type '{1}' to request type '{2}'", inject, inject.GetType(), type)); } 
0
source

Maybe something like:

 public IValueConverter<T> Create<T>(T type) { return _container.Resolve(typeof(IValueConverter<>).MakeGenericType(type.GetType())); } 

although I have not tested it.

0
source

In a completely different note, if you just call Convert under covers, why not just use System.Convert.ChangeType(Object, Type) ?

If you need to add support for custom types, you can create your own type converters and register them through TypeConverterAttribute .

0
source

This is usually done in the same way that Dryushkin avoided. You will need to register your open generic type with your container. With StructureMap, it would be something like this:

 Scan(scanner => { scanner.TheCallingAssembly(); scanner.ConnectImplementationsToTypesClosing(typeof (IValueConverter<>)); }); 

In Autofac, it will be something like this:

 builder.RegisterGeneric(typeof(IValueConverter<>)); 

Then you will create your general type for the solution:

 Type openType = typeof(IValueConverter<>); Type closedType = openType.MakeGenericType(type); var instance = container.Resolve(closedType); 

I donโ€™t think you want your factory method parameter to be generic. It just needs to be a simple type:

 public IValueConverter<T> Create<T>(Type type) { // ... } 

Instead of creating all this, why not just use Automapper? Here are the types of mappings that I usually create:

 public class DateTimeToDateMapping : IAutoMapperInitializer { public void Initialize(IConfiguration configuration) { configuration.CreateMap<DateTime, Date>().ConstructUsing( dateTime => new Date(dateTime.Year, dateTime.Month, dateTime.Day)); } } 

Here's how to use this mapping:

 var date = _mappingEngine.Map<DateTime, Date>(DateTime.Today); 

I donโ€™t know that I used System.Convert a lot, but it doesnโ€™t seem like a hint of intent, and I guess itโ€™s hard to know how to convert from certain things. If you use Automapper, you can also easily check your mappings.

0
source

Source: https://habr.com/ru/post/1342685/


All Articles