Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keep a Dictionary<Type, MyClass<T>> where elements are referenceable by type

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)

like image 820
Stuart Meeks Avatar asked Oct 23 '13 14:10

Stuart Meeks


3 Answers

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();
like image 110
Thomas Levesque Avatar answered Nov 16 '22 19:11

Thomas Levesque


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>();
   //...
}
like image 5
decPL Avatar answered Nov 16 '22 20:11

decPL


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();
    }
}
like image 3
Matan Shahar Avatar answered Nov 16 '22 18:11

Matan Shahar