I would like to create a little class factory what uses a dictionary, but I'm stuck on a point where I have to divide the creation of an instance and get an instance from the dictionary.
Instantiate looks like this:
public void CreateInstance<T>(string id) where T : class, new()
{
lock (_syncroot)
{
_internal_dict.Add(id, new T());
}
}
It's working well, but the problem is, that what can I do if I would like to instantiate a class what has to be parameterized constructor?
Is there any solution for it, or an architectural best practice?
With no contraints on T
other than being a class
your only real option is to use Refelction via the Activator
class.
//Still used for the no parameter constructor checking.
public void CreateInstance<T>(string id) where T : class, new()
{
lock (_syncroot)
{
_internal_dict.Add(id, new T());
}
}
public void CreateInstance<T>(string id, params object[] args) where T : class
{
lock (_syncroot)
{
_internal_dict.Add(id, Activator.CreateInstance(typeof(T), args));
}
}
Note that this removes the ability to do compile time checking that the class you are choosing for <T>
has a proper public constructor that can be called and may throw a exception at run-time if no constructor can be found.
It is not possible to constrain a type parameter so that it requires a class to have a parameterized constructor.
The best you can do is to require the class to implement an interface which has a factory method, then call the factory method from inside the generic method.
From "Essential C#", 4th edition:
public class EntityBase<TKey>
{
public EntityBase(TKey key)
{
Key = key;
}
public TKey Key
{
get {return _key;}
set {_key = value;}
}
private TKey _key;
}
public class EntityDictionary<TKey, TValue, TFactory> :
Dictionary<TKey, TValue>
where TKey : IComparable<T>, IFormattable
where TValue : EntityBase<TKey>
where TFactory : IEntityFactory<TKey, TValue>, new()
{
public TValue New(TKey key)
{
TValue newEntity = new TFactory().CreateNew(key);
Add(newEntity.Key, newEntity);
return newEntity;
}
}
public interface IEntityFactory<TKey, TValue>
{
TValue CreateNew(TKey key);
}
The EntityDictionary
class needs to create an instance of EntityBase
, but that requires a parameterized constructor. To get around that, create a class that implements IEntityFactory<TKey, TValue>
, and which has a default constructor. That class will know how to call the EntityBase
constructor.
It seems to me that you are trying to create an object repository, not a factory. For a repository it is easier to instantiate the objects before adding them to repository.
repository.Add(key, new SomeType(arg1, arg2));
If all the objects to be added to the repository derive from a common type or implement some common interface then simply use a typed Dictionary<TKey, TValue>
. Example:
private Dictionary<string, IGameObject> _gameObjects =
new Dictionary<string, IGameObject>();
_gameObjects.Add("car", new Car(maxSpeed));
_gameObjects.Add("monster", new Monster());
And retrieve it with:
IGameObject obj = _gameObjects(key);
If you intend to store completely unrelated types then use a compound key consisting of the type and possibly an identifier.
public class Repository
{
private class RepositoryKey
{
public RepositoryKey(Type type, string key)
{
this.Type = type;
this.Key = key;
}
public Type Type { get; private set; }
public string Key { get; private set; }
//TODO: override Equals and GetHashCode.
}
private Dictionary<RepositoryKey, object> _dict =
new Dictionary<RepositoryKey, object>();
public void Add<T>(string key, T obj)
{
_dict.Add(new RepositoryKey(typeof(T), key), obj);
}
public Get<T>(string key)
{
return (T)_dict[new RepositoryKey(typeof(T), key)];
}
}
This is just a minimalistic implementation with no error handling.
var rep = new Repository();
rep.Add("slow", new Car(30));
rep.Add("fast", new Car(180));
rep.Add("left", new Monster(x1, y1));
rep.Add("right", new Monster(x2, y2));
...
Car fastCar = rep.Get<Car>("fast");
Monster leftMonster = rep.Get<Monster>("left");
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.