Храните словарь <Type, MyClass <T>>, где элементы ссылочными по типу
У меня есть абстрактный класс EntityTypeTransform с единственным абстрактным методом, предназначенным для размещения делегата Func, который преобразует IDataRecord в экземпляр T.
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 dataRecord => new TaskParameter()
{
TaskId = (int)dataRecord["task_id"],
Name = (string)dataRecord["p_name"],
Value = (string)dataRecord["p_value"]
};
}
}
Теперь я хочу сохранить экземпляр каждого из этих классов в общем словаре, например:
Dictionary<Type, EntityTypeTransform<T>>
Но это не работает, потому что (например) экземпляр EntityTypeTransform of Task не совпадает с экземпляром EntityTypeTransform TaskParameter.
Может ли кто-нибудь помочь мне?
Изменить: я должен добавить, что Type key = typeof (T)
Ответы
Ответ 1
На самом деле, вам не нужно использовать словарь вообще! Вы можете использовать тот факт, что GenericClass<T>
на самом деле является другим типом для каждого T, поэтому он может иметь свои собственные статические поля (т.е. GenericClass<Foo>.SomeField
не используется совместно с GenericClass<Bar>.SomeField
)
Например, вы можете реализовать свой кеш следующим образом:
static class TransformCache<TEntityType>
{
public static EntityTypeTransform<TEntityType> Transform { get; set; }
}
И используйте его следующим образом:
TransformCache<TaskParameter>.Transform = new TaskParameterEntityTypeTransform();
Ответ 2
Вы не можете указать массивную типизированную коллекцию, которая будет содержать разные типы. Здесь подход, который я использовал в подобной проблеме, изменен в соответствии с вашим требованием:
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>;
}
}
Это дает вам безопасный кэш для вашего общего типа (хотя безопасность типов обеспечивается логикой класса, а не С#). Вы можете использовать его следующим образом:
var collection = new TransformCollection();
collection.Add(SomeMethodToGetTransform<Task>());
//...
if (collection.Exists<Task>())
{
var transform = collection.Get<Task>();
//...
}
Ответ 3
Вы можете использовать интерфейс, который не является универсальным, а затем реализовать этот интерфейс явно внутри этого абстрактного класса. Он довольно часто встречается в самой библиотеке .Net:
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();
}
}
Ответ 4
Вам нужно будет создать базовый класс, отличный от общего, например
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"]
};
}
}
Теперь вы можете создать словарь:
var d = new Dictionary<Type, EntityTypeTransformBase>();
d.Add(typeof(TaskParameter), new TaskParameterEntityTypeTransform());
Ответ 5
Вы можете использовать KeyedByTypeCollection
, чтобы получить безопасность по типу, и вы можете определить интерфейс с параметром ковариационного типа, чтобы убедиться, что только объекты типа EntityTypeTransform<T>
можно добавить в словарь:
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
};
}
Теперь вы можете использовать его следующим образом:
public void SomeMethod(IDataRecord dataRecord)
{
TaskParameter taskParameter = TransformDictionary.Find<TaskParameterEntityTypeTransform>().Transform(dataRecord);
}
Ответ 6
Добавьте к вашим трансформаторам не общий интерфейс:
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"],
};
}
}
Затем вы можете инкапсулировать словарь, чтобы гарантировать соответствие типов данных. Никогда не позволяйте добавлять IEntityTypeTransform плохого типа:
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);
}
}
}
Преимущество заключается в том, что во время компиляции вы уверены, что никто не может вставить плохой трансформатор, даже если вы не используете дженерики.
Использование:
var transforms = new TransformDistributor();
transforms.Add<TaskParameter>(new TaskParameterEntityTypeTransform());
var taskParameter = transforms.Transform<TaskParameter>(new DataRecord());
Ответ 7
Я попытался понять, чего вы точно хотите, я надеюсь, что это именно то, что вы ищете!
Вы должны установить в классе TaskParameter правильные параметры: 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;