简体   繁体   中英

C# Factory of classes that implement generic Interface

I have an interface defined like this:

    public interface IEntitySetMgr<T> {

    /// <summary>
    /// The columns involved in the query. Only fields in those columns
    /// will be retrieved from the db.
    /// </summary>
    List<ColumnInfo> Columns { get; set; }

    /// <summary>
    /// The number of entities found in the last query to db.
    /// </summary>
    int EntityCount { get; }

    bool Init();

    bool Synch( QueryFilter queryFilter = null );

    /// <summary>
    /// Retrieves the entities build from data of db.
    /// </summary>
    ObservableCollection<T> GetEntities();
}

I want some classes to implement that interface, and I want to be able to use a factory to register and create instances of those registered classes. I have this Factory, but seems not to compile:

    public class EntitySetMgrFactory {

    public delegate IEntitySetMgr<T> EntitySetMgrCreator<T>();

    private Dictionary<string, EntitySetMgrCreator<T>> _creators = 
        new Dictionary<string, EntitySetMgrCreator<T>>();

    private static EntitySetMgrFactory _instance = null;
    public static EntitySetMgrFactory Instance {
        get {

            if( _instance == null ) {

                _instance = new EntitySetMgrFactory();
            }

            return _instance;
        }
    }

    private EntitySetMgrFactory() { }

    public bool registerEntitySetMgr<T>( string setName, EntitySetMgrCreator<T> creator ) {

        if( !_creators.ContainsKey( setName ) ) {

            _creators[setName] = creator;
            return true;
        }

        return false;
    }

    public IEntitySetMgr<T> getEntitySetMgr<T>( string setName ) {

        if( _creators.ContainsKey( setName ) ) {

            return (IEntitySetMgr<T>)( _creators[setName] )();
        }

        return null;
    }
}

It complains in the definition of the dictionary. If I change T by object in the dictionary it does not complains, but complains when trying to add a EntitySetMgrCreator to the dictionary, saying there is not available conversion. I try to put the explicitly the cast but does not work either.

I've seen examples that use Activator to create the instances by knowing the type beforehand and then casting the type, but I would really want to have a method that create the instance and register that method instead, so that I don't have to know each type beforehand.

I have seen something about covariants, but I think this would break my interface as I need a method for returning an observable collection of entities.

Thanks for your help.

Regards,

David.

When you write this:

private Dictionary<string, EntitySetMgrCreator<T>> _creators = 
        new Dictionary<string, EntitySetMgrCreator<T>>();

You don't really have a T because you define your class as EntitySetMgrFactory .

You probably have to rethink how you want to do it and what you really want to do here. You could just simply say

private Dictionary<string, object> _creators = 
        new Dictionary<string, object>();

Then

public IEntitySetMgr<T> getEntitySetMgr<T>( string setName ) {

        if( _creators.ContainsKey( setName ) ) {

            return ((EntitySetMgrCreator<T>) _creators[setName] )();
        }

        return null;
    }

But it is not clear for me what is your goal.

Another problem with the code is that your singleton implementation is not thread safe. Please check this: http://csharpindepth.com/Articles/General/Singleton.aspx

You can't achieve this without either making the factory generic:

EntitySetMgrFactory<T> and removing the T from registerEntitySetMgr

or

making the dictionary of a non-generic type and then doing down casting at runtime.

First solution:

public class EntitySetMgrFactory<T>
{

    public delegate IEntitySetMgr<T> EntitySetMgrCreator<T>();

    private readonly Dictionary<string, EntitySetMgrCreator<T>> _creators = new Dictionary<string, EntitySetMgrCreator<T>>();

    private static EntitySetMgrFactory<T> _instance;
    public static EntitySetMgrFactory<T> Instance => _instance ?? (_instance = new EntitySetMgrFactory<T>());

    private EntitySetMgrFactory() { }

    public bool registerEntitySetMgr(string setName, EntitySetMgrCreator<T> creator)
    {
        if (_creators.ContainsKey(setName)) return false;
        _creators[setName] = creator;
        return true;
    }

    public IEntitySetMgr<T> getEntitySetMgr<T>(string setName) => _creators.ContainsKey(setName) ? (IEntitySetMgr<T>)_creators[setName]() : null;
}

Second solution:

public class EntitySetMgrFactory
{

    public delegate IEntitySetMgr<T> EntitySetMgrCreator<T>();

    private readonly Dictionary<string, object> _creators = new Dictionary<string, object>();

    private static EntitySetMgrFactory _instance;
    public static EntitySetMgrFactory Instance => _instance ?? (_instance = new EntitySetMgrFactory());

    private EntitySetMgrFactory() { }

    public bool registerEntitySetMgr<T>(string setName, EntitySetMgrCreator<T> creator)
    {
        if (_creators.ContainsKey(setName)) return false;
        _creators[setName] = creator;
        return true;
    }

    public IEntitySetMgr<T> getEntitySetMgr<T>(string setName) => _creators.ContainsKey(setName) ? ((EntitySetMgrCreator<T>)_creators[setName])() : null;
}

Unfortunatly this won't work. The dictionary as you declared it needs a specific type for T . So Uno is already right. But I guess a generic factory is not what you want, as you probably want one factory that builds your generic classes for different types.

So I suggest to make the dictionary non-generic inside the factory, but keep it type safe to the outside:

public class EntitySetMgrFactory
{
    public delegate IEntitySetMgr<T> EntitySetMgrCreator<T>();

    private Dictionary<string, object> _creators = 
        new Dictionary<string, object>();

    public bool registerEntitySetMgr<T>(string setName, EntitySetMgrCreator<T> creator)
    {
        if(_creators.ContainsKey(setName)) return false;
        _creators[setName] = creator;
        return true;
    }

    public IEntitySetMgr<T> getEntitySetMgr<T>(string setName)
    {
        return _creators.ContainsKey(setName) 
              ? (_creators[setName] as EntitySetMgrCreator<T>)?.Invoke()
              : null;
    }

    // shortened for clarity, your constructor etc here...
}

So in getEntitySetMgr<T> I cast the object in the dictionary back to your delegate type and if this is not null , invoke the delegate and return the result.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM