I have a generic base abstract class:
public abstract class Generator<T>
{
public abstract void Start(T config);
}
Then, I have many concrete classes that inherit from base class and expect certain parameter type. Some of them:
public class AGenerator : Generator<AGeneratorConfig>
{
public override void Start(AGeneratorConfig Config) { /* some code*/ }
}
public class BGenerator : Generator<BGeneratorConfig>
{
public override void Start(BGeneratorConfig Config) { /* some code*/ }
}
Their Start() method parameters are defined as follows:
public abstract class GeneratorConfig
{
public int CommonProperty {get; set;}
}
public class AGeneratorConfig : GeneratorConfig
{
// Some props specific for AGenerator
}
public class BGeneratorConfig : GeneratorConfig
{
// Some props specific for BGenerator
}
At last, I have a client/manager/factory-like class that handles actual generator start process with provided config,
but is uses casting concrete to abstract generic class:
public class GeneratorClient
{
public static void StartGenerator<T>(T config)
{
Generator<T> generator = null;
if (config is AGeneratorConfig)
{
generator = new AGenerator() as Generator<T>; // casting to abstract base class
}
else if (config is BGeneratorConfig)
{
generator = new BGenerator() as Generator<T>; // casting to abstract base class
}
else
{
throw new NotImplementedException();
}
generator.Start(config);
}
}
My question: is there any workaround to eliminate the need of casting concrete to abstract base classes?
The most simple solution would be this:
public static void StartGenerator<T>(T config)
{
if (config is AGeneratorConfig)
{
var generator = new AGenerator();
generator.Start(config);
}
else if (config is BGeneratorConfig)
{
var generator = new BGenerator();
generator.Start(config);
}
else
{
throw new NotImplementedException();
}
}
but for each newly created concrete Generator object generator.Start(config); needs to be repeated.
Define the generator just as object and cast it to Generator<T> only at the last call:
public static void StartGenerator<T>(T config)
{
object generator = null;
if (config is AGeneratorConfig)
{
generator = new AGenerator();
}
else if (config is BGeneratorConfig)
{
generator = new BGenerator();
}
else
{
throw new NotImplementedException();
}
((Generator<T>)generator).Start(config);
}
You can use Reflection to call the constructor, and maybe improve you Factory in order to remove those if's.
You can use a dictionary like this:
private Dictionary<Type, Type> Diccionary;
public void CreateDicionary()
{
Diccionary = new Dictionary<Type, Type>();
Diccionary.Add(typeof(AGeneratorConfig), typeof(AGenerator));
Diccionary.Add(typeof(BGeneratorConfig), typeof(BGenerator));
}
then you can have the concrete instance with something like this:
public Generator<T> GetGenerator<T>()
{
var type = typeof(T);
if (!Diccionary.ContainsKey(type))
throw new Exception("Not found");
var typeInstance = Diccionary[type];
return (Generator<T>) Activator.CreateInstance(typeInstance);
}
And you use the code above with something similar to this:
public void StartGenerator<T>(T config)
{
var generator = GetGenerator<T>();
generator.Start(config);
}
public static void Main()
{
var gen = new GeneratorClient();
gen.CreateDicionary();
gen.StartGenerator<AGeneratorConfig>(new AGeneratorConfig());
}
Related
I am new in C# Generic concept and I would like to return interface implemented class using generic concept. Below is my example which is currently implemented without generic:
1) Factory Class which return interface and this class has two overload method which accept different data model:
public class Factory
{
public ICommon Init(DBInfoData dbInfoData)
{
return new ClassA(dbInfoData);
}
public ICommon Init(WebInfoData webInfoData)
{
return new ClassB(webInfoData);
}
}
2) Interface and interface implemented two class as below:
//=== Common Interface
public interface ICommon
{
void MethodA();
void MethodB();
}
//=== Internal access only ClassA
internal class ClassA : ICommon
{
private DBInfoData _DBInfoData = null;
public ClassA(DBInfoData dbInfoData)
{
_DBInfoData = dbInfoData;
}
public void MethodA()
{
throw new NotImplementedException();
}
public void MethodB()
{
throw new NotImplementedException();
}
}
//=== Internal access only ClassB
internal class ClassB : ICommon
{
private WebInfoData _WebInfoData = null;
public ClassB(WebInfoData webInfoData)
{
_WebInfoData = webInfoData;
}
public void MethodA()
{
throw new NotImplementedException();
}
public void MethodB()
{
throw new NotImplementedException();
}
}
3) Data Model class as below:
//=== Database Information
public class DBInfoData
{
public string Server { get; set; }
public string Database { get; set; }
}
//=== Web Server Information
public class WebInfoData
{
public string URL { get; set; }
public int Port { get; set; }
}
Now I want to implement generic functionality of C# where in factory class I do not want to declare two overload method. Using single method I can return ClassA or ClassB based on Data Model pass.
You could edit the Init method without having to edit anything else. This method will take a generic type parameter T, which can be of any type. Then you can use the is operator, which according to the docs used to type testing. You need to check however for any unsupported type of T, because you didn't add any constraint to the generic type passed. A raw implementation would be:
public class Factory
{
public ICommon Init<T>(T infoData)
{
if (infoData is DBInfoData dbInfoData) {
return new ClassA(dbInfoData);
}
if (infoData is WebInfoData webInfoData) {
return new ClassB(webInfoData);
}
throw new Exception($"Cannot create instance for info data of type {infoData.GetType().Name}");
}
}
And to test it:
var factory = new Factory();
var t1 = factory.Init(new DBInfoData()); // will be ClassA
var t2 = factory.Init(new WebInfoData()); // ClassB
To sophisticate it, you could introduce type constraint on your generic T class to make sure you can only pass appropriate types. For the current situation, you could create a marker interface for your classes DBInfoData and WebInfoData by introducing an empty interface say IInfoData. Then you have to inherit your classes like this:
public interface IInfoData {}
public class DBInfoData : IInfoData
{
public string Server { get; set; }
public string Database { get; set; }
}
public class WebInfoData : IInfoData
{
public string URL { get; set; }
public int Port { get; set; }
}
Now both inherits from (actually 'marked by') your base interface. Introduce a constraint to your factory to allow only descendants of IInfoData to be passed as an argument (so either DBInfoData or WebInfoData) by adding a constraint shown in the docs I linked above:
public class Factory
{
public ICommon Init<T>(T infoData) where T: IInfoData
{
if (infoData is DBInfoData dbInfoData) {
return new ClassA(dbInfoData);
}
if (infoData is WebInfoData webInfoData) {
return new ClassB(webInfoData);
}
throw new Exception($"Cannot create instance for info data of type {infoData.GetType().Name}");
}
}
Any type other than the descendants of IInfoData will cause a compilation error, and you're done. Use it like in my previous example:
var factory = new Factory();
var t1 = factory.Init(new DBInfoData()); // will be ClassA
var t2 = factory.Init(new WebInfoData()); // ClassB
I'm developing a library for developers where they have to create a class that inherits from a class I created.
This base class essentially manages an array of objects for the developer, however the developer gets to specify the type of these objects they want the base class to manage.
So the developer essentially just tells the base class to create an array, then only has read only access to that array. The base class will (depending on the state of the application) add or remove objects from the array.
I'm stuck at finding the right data type to store such a thing. I've tried ref and out but that got me nowhere. The closest I got was with a Dictionary but that idea fell apart because C# is actually just copying the value into the dictionary instead of referencing or pointing to it.
Here is a quick example I threw together:
public static void Main()
{
Derived d = new Derived();
d.InitBase();
d.Init();
d.CheckArray();
d.AddElement<GenericObject>(new GenericObject{ i = 2 });
d.CheckArray();
}
public class Base {
Dictionary<Type, List<object>> ArrayReferences;
public void InitBase() {
ArrayReferences = new Dictionary<Type, List<object>>();
}
protected ReadOnlyCollection<T> RegisterArray<T>() {
List<object> managedArray = new List<object>();
ArrayReferences.Add(typeof(T), managedArray);
return Array.AsReadOnly(managedArray.Select(s => (T)s).ToArray());
}
public void AddElement<T>(T obj) {
ArrayReferences[typeof(T)].Add(obj);
}
public void RemoveElement<T>(T obj) {
ArrayReferences[typeof(T)].Remove(obj);
}
}
public class Derived: Base {
ReadOnlyCollection<GenericObject> arr;
public void Init() {
arr = RegisterArray<GenericObject>();
}
public void CheckArray() {
Console.WriteLine(arr.Count());
}
}
public class GenericObject {
public int i = 0;
}
Output:
0
0
Dictionary obviously doesn't store the values as references like I want it to. So what other technique does C# have or is this simply not possible? Also not sure how many issues unsafe will cause me so I'm scared going that route.
While I think there are better ways of handling this issue, this can be done.
Instead of storing a List<object> reference, which isn't compatible with a List<T>, store an object. Use a static in Base to hold the Dictionary so there is one Dictionary for all derived classes.
public static void Main() {
var d = new Derived();
d.CheckCollection("d before AddElement");
d.AddElement(new GenericObject { i = 2 });
d.CheckCollection("d after AddElement");
Console.WriteLine($"ListCount = {Base.ListCount}");
var d2 = new Derived2();
d2.CheckCollection("d2 before AddElement");
d2.AddElement(new GenericObject2 { i = 4 });
d2.AddElement(new GenericObject2 { i = 5 });
d2.CheckCollection("d2 after AddElement");
Console.WriteLine($"ListCount = {Base.ListCount}");
}
public class Base {
static Dictionary<Type, object> ListReferences = new Dictionary<Type, object>();
public static int ListCount => ListReferences.Count();
protected ReadOnlyCollection<T> RegisterList<T>() {
var managedList = new List<T>();
ListReferences.Add(typeof(T), managedList);
return managedList.AsReadOnly();
}
public void AddElement<T>(T obj) {
((List<T>)ListReferences[typeof(T)]).Add(obj);
}
public void RemoveElement<T>(T obj) {
((List<T>)ListReferences[typeof(T)]).Remove(obj);
}
}
public class Derived : Base {
ReadOnlyCollection<GenericObject> roc;
public Derived() {
roc = RegisterList<GenericObject>();
}
public void CheckCollection(string msg) {
Console.WriteLine(msg);
Console.WriteLine(roc.Count());
}
}
public class Derived2 : Base {
ReadOnlyCollection<GenericObject2> roc;
public Derived2() {
roc = RegisterList<GenericObject2>();
}
public void CheckCollection(string msg) {
Console.WriteLine(msg);
Console.WriteLine(roc.Count());
}
}
public class GenericObject {
public int i = 0;
}
public class GenericObject2 {
public int i = 0;
}
PS Also, don't name methods and variables with "array" when you are using Lists.
The following code you've written makes a copy of your list at the time you created it - so it is always empty, no matter what you add to the list afterwards.
List<object> managedArray = new List<object>();
ArrayReferences.Add(typeof(T), managedArray);
return Array.AsReadOnly(managedArray.Select(s => (T)s).ToArray());
Here is how you should write your code to get what you want:
public static void Main()
{
Derived d = new Derived();
Console.WriteLine(d.AsReadOnly().Count);
d.AddElement(new GenericObject { i = 2 });
Console.WriteLine(d.AsReadOnly().Count);
}
public class Base<T>
{
List<T> _items = new List<T>();
public ReadOnlyCollection<T> AsReadOnly()
{
return Array.AsReadOnly(_items.ToArray());
}
public void AddElement(T obj)
{
_items.Add(obj);
}
public void RemoveElement(T obj)
{
_items.Remove(obj);
}
}
public class Derived : Base<GenericObject>
{
}
public class GenericObject
{
public int i = 0;
}
That outputs:
0
1
Now, it's worth considering that List<T> already has a AsReadOnly() method, so you could simply write this:
public static void Main()
{
var d = new List<GenericObject>();
Console.WriteLine(d.AsReadOnly().Count);
d.Add(new GenericObject { i = 2 });
Console.WriteLine(d.AsReadOnly().Count);
}
public class GenericObject
{
public int i = 0;
}
That works too.
Here's how you should do this to hold more than one list at a time. There's no need for inheritance.
public static void Main()
{
Repository r = new Repository();
Console.WriteLine(r.AsReadOnly<GenericObject>().Count);
r.AddElement<GenericObject>(new GenericObject { i = 2 });
Console.WriteLine(r.AsReadOnly<GenericObject>().Count);
}
public class Repository
{
private Dictionary<Type, object> _references = new Dictionary<Type, object>();
private void Ensure<T>()
{
if (!_references.ContainsKey(typeof(T)))
{
_references[typeof(T)] = new List<T>();
}
}
public ReadOnlyCollection<T> AsReadOnly<T>()
{
this.Ensure<T>();
return (_references[typeof(T)] as List<T>).AsReadOnly();
}
public void AddElement<T>(T obj)
{
this.Ensure<T>();
(_references[typeof(T)] as List<T>).Add(obj);
}
public void RemoveElement<T>(T obj)
{
this.Ensure<T>();
(_references[typeof(T)] as List<T>).Remove(obj);
}
}
public class GenericObject
{
public int i = 0;
}
In your base (or encapsulated class if you choose to go that way):
protected ReadOnlyCollection<T> GetSnapshot<T>() {
return Array.AsReadOnly(ArrayReferences[typeof(T)].Select(s => (T)s).ToArray());
}
Then you'd also add any other methods to view the data, e.g. to get a count:
protected int GetCount<T>() {
return ArrayReferences[typeof(T)].Count;
}
I'm looking for RealProxy replacement in .NET Core, and this issue forwards me to DispatchProxy.
It has simple API, but it's unclear, how to wrap existing object into proxy.
E.g., having this interface:
interface IFoo
{
string Bar(int boo);
}
and this implementation:
class FooImpl : IFoo
{
public string Bar(int boo)
{
return $"Value {boo} was passed";
}
}
how to get what I want?
class Program
{
static void Main(string[] args)
{
var fooInstance = new FooImpl();
var proxy = DispatchProxy.Create<IFoo, FooProxy>();
var s = proxy.Bar(123);
Console.WriteLine(s);
}
}
class FooProxy : DispatchProxy
{
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
return targetMethod.Invoke(/* I need fooInstance here */, args);
}
}
Since DispatchProxy descendants must have parameterless constructor, the only idea I have is to invent some method, like this:
class FooProxy : DispatchProxy
{
private object target;
public void SetTarget(object target)
{
this.target = target;
}
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
return targetMethod.Invoke(target, args);
}
}
and use it this way:
var fooInstance = new FooImpl();
var proxy = DispatchProxy.Create<IFoo, FooProxy>();
((FooProxy)proxy).SetTarget(fooInstance);
// the rest of code...
Is this correct approach?
You are right that there is no other option here than to cast the generated IFoo to the known proxy type (FooProxy) and use a custom method or property on FooProxy. There is no public API to add constructor arguments or return the proxy as the implementation type. However, DispatchProxy.Create() will return an instance of a subclass of FooProxy whose type is generated at runtime via reflection and IL emitting.
If you are looking at other ways to quickly wrap an implementation and replace interface methods / virtual methods, I suggest using mocking frameworks instead (FakeItEasy, Moq, NSubstitute etc.).
You need to create your own Generic class that inherit from DispatchProxy and has own static Create that has an extra parameter from type target.
example
public class AopAction<T>:DispatchProxy
{
#region Private Fields
private Action<MethodInfo,object[],object> ActAfter;
private Action<MethodInfo,object[]> ActBefore;
private Action<MethodInfo,object[],Exception> ActException;
private T Decorated;
#endregion Private Fields
#region Public Methods
public static T Create(T decorated,Action<MethodInfo,object[]> actBefore = null,Action<MethodInfo,object[],object> actAfter = null,Action<MethodInfo,object[],Exception> actException = null)
{
object proxy = Create<T,AopAction<T>>();
SetParameters();
return (T)proxy;
void SetParameters()
{
var me = ((AopAction<T>)proxy);
me.Decorated = decorated == null ? throw new ArgumentNullException(nameof(decorated)) : decorated;
me.ActBefore = actBefore;
me.ActAfter = actAfter;
me.ActException = actException;
}
}
#endregion Public Methods
#region Protected Methods
protected override object Invoke(MethodInfo targetMethod,object[] args)
{
_ = targetMethod ?? throw new ArgumentException(nameof(targetMethod));
try
{
ActBefore?.Invoke(targetMethod,args);
var result = targetMethod.Invoke(Decorated,args);
ActAfter?.Invoke(targetMethod,args,result);
return result;
}
catch(Exception ex)
{
ActException?.Invoke(targetMethod,args,ex);
throw ex.InnerException;
}
}
#endregion Protected Methods
}
to use your example
var proxy=AopAction<IFoo>.Create(new FooImpl());
I have the following classes, and when I call CreateQuerySettings on the BaseScriptConfigurationList, it returns the new QuerySettings from ConfigurationList, rather than the HierarchicalQuerySettings value in BaseScriptConfigurationList:
public abstract class ConfigurationList<TConfigurationObject, TPropertyEnum>
{
public QuerySettings<TConfigurationObject, TPropertyEnum> CreateQuerySettings()
{
return new QuerySettings<TConfigurationObject, TPropertyEnum>();
}
}
public class BaseScriptConfigurationList : EditableConfigurationList<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
public BaseScriptConfigurationList(ConfigurationManager configurationManager)
: base(configurationManager, InternalAdminObjectType.BaseScript)
{
_BaseScriptPageListWatcher = new ConfigurationList<BaseScriptPageConfiguration, BaseScriptPageConfiguration.Property>.
ConfigurationWatcher(null);
_ConfigurationWatcher.ChildWatchers.Add(_BaseScriptPageListWatcher);
}
public new QuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property> CreateQuerySettings()
{
return new HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property, BaseScriptQueryChildrenSettings>();
}
}
Edit: I make the call from another class where TConfigurationObjectList is BaseScriptConfigurationList. I've added the constructor to the code above so you can see what it's doing. Please note that EditableConfigurationList inherits from ConfigurationList.
TConfigurationObjectList cl = (TConfigurationObjectList)typeof(TConfigurationObjectList).GetConstructor(new Type[] { typeof(ConfigurationManager) }).Invoke(new object[] { Manager.ConfigurationManager });
var querySettings = cl.CreateQuerySettings();
When I make this call, it goes into the ConfigurationList.CreateQuerySettings method.
How can I hide the CreateQuerySettings method, so that when I call it from the BaseScriptConfigurationList class, I get a HierarchicalQuerySettings object?
The new modifier can be beasty. Note that you are hiding and not overriding in your example. You are not showing that part of the code, but I assume you have this situation:
class Base
{
public static void BaseMethod() { Console.WriteLine("BASE!"); }
}
class Derived : Base
{
// Hides Base.BaseMethod()
new public static void BaseMethod() { Console.WriteLine("DERIVED!"); }
}
Base a = new Base();
a.BaseMethod(); // -> "BASE!"
Base b = new Derived();
b.BaseMethod(); // -> "BASE!"
Derived b = new Derived();
b.BaseMethod(); // -> "DERIVED!"
In BaseScriptConfigurationList.CreateQuerySettings()
you're return type is QuerySettings<T,T> so you will always get that type as a return value, but you are returning a HierarchicalQuerySettings. You can one, change the return type of CreateQuerySettings() to HierarchicalQuerySettings or two, cast the object to its child type "HierarchicalQuerySettings". If you really want to hide it, you can do this:
public class newclass : BaseScriptConfigurationList
{
public new HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property> CreateQuerySettings()
{
return (HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property>)base.CreateQuerySettings();
}
}
But that doesn't really seem efficient and i advise against it. Like i said, i maybe missing some other requirement, but based on the info that you gave..
Basically, what I'm seeing (and making assumptions) that TConfigurationObjectList Inhertis from ConfigurationList somewhere along the lines, so on and so forth, all the way up to EditableConfigurationList. since you are dynamically creating an instance of the class TConfigurationObjectList, and calling the method from that point, you will be calling the base ConfigurationList member CreateQuerySettings. You do not have access to the new CreateQuerySettings. If you are creating the class BaseScriptConfigurationList instance at this point, cast the object ((BaseScriptConfigurationList)cl).CreateQuerySettings(). That being said. if you do not know what you have at runtime:
var obj = typeof(TConfigurationObjectList).GetConstructor(new Type[] { typeof(ConfigurationManager) }).Invoke(new object[] { Manager.ConfigurationManager });
var cl = (obj as BaseScriptConfigurationList) ?? (TConfigurationObjectList)obj;
// or do something else
var querySettings = cl.CreateQuerySettings();
Note i am assuming your architecture is roughly set up like this:
public abstract class ConfigurationList<TConfigurationObject, TPropertyEnum>
{
public QuerySettings<TConfigurationObject, TPropertyEnum> CreateQuerySettings()
{
return new QuerySettings<TConfigurationObject, TPropertyEnum>();
}
}
public class TConfigurationObjectList : ConfigurationList<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
}
public class EditableConfigurationList<T, T1> : TConfigurationObjectList
{
protected EditableConfigurationList(ConfigurationManager configurationManager, object baseScript)
{
throw new NotImplementedException();
}
}
public class BaseScriptConfigurationList : EditableConfigurationList<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
public BaseScriptConfigurationList(ConfigurationManager configurationManager)
: base(configurationManager, InternalAdminObjectType.BaseScript)
{
}
public new QuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property> CreateQuerySettings()
{
return new HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property, BaseScriptQueryChildrenSettings>();
}
}
public class QuerySettings<T, T1>
{
}
public class HierarchicalQuerySettings<T, T1, T2> : QuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
}
public class BaseScriptQueryChildrenSettings
{
}
public class BaseScriptPageConfiguration
{
public class Property
{
}
}
public class InternalAdminObjectType
{
public static object BaseScript { get; set; }
}
public class ConfigurationManager
{
}
public class BaseScriptConfiguration
{
public class Property
{
}
}
Create a base interface for the ConfigurationList class (say IConfigurationList) and use this interface as the data type for the variable cl instead of TConfigurationList.
I have an abstract class called EntityTypeTransform with a single abstract method designed to hold a Func delegate that converts an IDataRecord into an instance of T.
public abstract class EntityTypeTransform<TEntityType> where TEntityType : class
{
public abstract Func<IDataRecord, TEntityType> GetDataTransform();
}
An implementation of that class might look like (does look like) this:
public class TaskParameterEntityTypeTransform : EntityTypeTransform<TaskParameter>
{
public override Func<IDataRecord, TaskParameter> GetDataTransform()
{
return dataRecord => new TaskParameter()
{
TaskId = (int)dataRecord["task_id"],
Name = (string)dataRecord["p_name"],
Value = (string)dataRecord["p_value"]
};
}
}
Now I want to keep an instance of each of these classes in a generic Dictionary, something like:
Dictionary<Type, EntityTypeTransform<T>>
But this doesn't work because (for example) an instance of EntityTypeTransform Of Task is not the same as an instance of EntityTypeTransform Of TaskParameter.
Can anyone help me out?
Edit: I should add that the Type key = typeof(T)
Actually, you don't need to use a dictionary at all! You can use the fact that GenericClass<T> is actually a different type for each T, so it can have its own static fields (i.e. GenericClass<Foo>.SomeField is not shared with GenericClass<Bar>.SomeField)
For instance you can implement your cache like this:
static class TransformCache<TEntityType>
{
public static EntityTypeTransform<TEntityType> Transform { get; set; }
}
And use it like this:
TransformCache<TaskParameter>.Transform = new TaskParameterEntityTypeTransform();
You can't specify a strong-typed collection that would hold different generic types. Here's the approach I've used in a similar problem, modified to match your requirement:
class TransformCollection
{
private Hashtable cache = new Hashtable();
public void Add<T>(EntityTypeTransform<T> transform) where T : class
{
this.cache[typeof(T)] = itemToCache;
}
public bool Exists<T>() where T : class
{
return this.cache.ContainsKey(typeof(T));
}
public EntityTypeTransform<T> Get<T>() where T : class
{
if (!this.Exists<T>())
throw new ArgumentException("No cached transform of type: " + typeof(T).Name);
return this.cache[typeof(T)] as EntityTypeTransform<T>;
}
}
This gives you type-safe cache for your generic type (though type-safety is enforced by the class's logic, not C#). You can use it as follows:
var collection = new TransformCollection();
collection.Add(SomeMethodToGetTransform<Task>());
//...
if (collection.Exists<Task>())
{
var transform = collection.Get<Task>();
//...
}
You could use an interface that is non-generic and then implement that interface explicitly inside that abstract class, It's pretty common in the .Net library itself:
public interface IEntityTypeTransform
{
Func<IDataRecord, object> GetDataTransform();
}
public abstract class EntityTypeTransform<TEntityType> : IEntityTypeTransform
where TEntityType : class
{
public virtual Func<IDataRecord, TEntityType> GetDataTransform()
{
return this.GetDataTransformImpl();
}
public abstract Func<IDataRecord, TEntityType> GetDataTransformImpl();
Func<IDataRecord, object> IEntityTypeTransform.GetDataTransform()
{
return this.GetDataTransform();
}
}
You would have to create a non-generic base class, e.g.
public abstract class EntityTypeTransformBase
{
public abstract Func<IDataRecord, object> GetDataTransform();
}
public abstract class EntityTypeTransform<TEntityType> : EntityTypeTransformBase where TEntityType : class
{
public abstract Func<IDataRecord, TEntityType> GetDataTransformImpl();
public override Func<IDataRecord, object> GetDataTransform()
{
return GetDataTransformImpl();
}
}
public class TaskParameterEntityTypeTransform : EntityTypeTransform<TaskParameter>
{
public override Func<IDataRecord, TaskParameter> GetDataTransformImpl()
{
return dataRecord => new TaskParameter()
{
TaskId = (int)dataRecord["task_id"],
Name = (string)dataRecord["p_name"],
Value = (string)dataRecord["p_value"]
};
}
}
Now you can create your dictionary:
var d = new Dictionary<Type, EntityTypeTransformBase>();
d.Add(typeof(TaskParameter), new TaskParameterEntityTypeTransform());
You can use KeyedByTypeCollection to get type-safety and you can define an interface with a covariant type parameter to make sure that only objects of type EntityTypeTransform<T> can be added to the dictionary:
public interface IEntityTypeTransform<out TEntityType> where TEntityType : class
{
TEntityType Transform(IDataRecord dataRecord);
}
public abstract class EntityTypeTransform<TEntityType> : IEntityTypeTransform<TEntityType> where TEntityType : class
{
public abstract TEntityType Transform(IDataRecord dataRecord);
}
public class TaskParameter
{
public int TaskId;
public string Name;
public string Value;
}
public class TaskParameterEntityTypeTransform : EntityTypeTransform<TaskParameter>
{
public override TaskParameter Transform(IDataRecord dataRecord)
{
return new TaskParameter()
{
TaskId = (int)dataRecord["task_id"],
Name = (string)dataRecord["p_name"],
Value = (string)dataRecord["p_value"]
};
}
}
public class SomeClass
{
public KeyedByTypeCollection<IEntityTypeTransform<object>> TransformDictionary = new KeyedByTypeCollection<IEntityTypeTransform<object>>()
{
new TaskParameterEntityTypeTransform(),
// More transforms here
};
}
Now you can use it like this:
public void SomeMethod(IDataRecord dataRecord)
{
TaskParameter taskParameter = TransformDictionary.Find<TaskParameterEntityTypeTransform>().Transform(dataRecord);
}
I have tried to understand what you exactly want I hope this is exactly what you are looking for!
You shall set in TaskParameter class the correct parameters: TaskId, Name, Value
public abstract class EntityTypeTransform<TEntityType> where TEntityType : class
{
public abstract Func<IDataRecord, TEntityType> GetDataTransform();
}
public class TaskParameterEntityTypeTransform : EntityTypeTransform<TaskParameter>
{
public override Func<IDataRecord, TaskParameter> GetDataTransform()
{
return x => new TaskParameter { X = x.FieldCount };
}
}
public class TaskParameter
{
public int X { get; set; }
}
Dictionary<Type, EntityTypeTransform<TaskParameter>> imADict;
Add a non generic interface to your transformers:
public interface IEntityTypeTransform
{
Func<IDataRecord, object> GetDataTransform();
}
public abstract class EntityTypeTransform<T> : IEntityTypeTransform
{
public abstract Func<IDataRecord, object> GetDataTransform();
}
public class TaskParameterEntityTypeTransform : EntityTypeTransform<TaskParameter>
{
public override Func<IDataRecord, object> GetDataTransform()
{
return dataRecord => new TaskParameter()
{
TaskId = (int)dataRecord["task id"],
};
}
}
Then you can encapsulate your dictionary for ensure that datatypes will always match. Never allow to add a IEntityTypeTransform of a bad type :
public class TransformDistributor
{
private readonly Dictionary<Type, IEntityTypeTransform> _transforms = new Dictionary<Type, IEntityTypeTransform>();
public void Add<T>(EntityTypeTransform<T> type)
{
this._transforms.Add(typeof(T), type);
}
public T Transform<T>(IDataRecord record)
{
var transform = this._transforms[typeof(T)].GetDataTransform()(record);
if (transform is T)
{
return (T)transform;
}
else
{
// theorically can't happen
throw new InvalidOperationException("transformer doesn't return instance of type " + transform.GetType().Name);
}
}
}
The advantage are that at compile time, your are sure that nobody can insert a bad transformer, even if your are not using generics.
Usage :
var transforms = new TransformDistributor();
transforms.Add<TaskParameter>(new TaskParameterEntityTypeTransform());
var taskParameter = transforms.Transform<TaskParameter>(new DataRecord());