C # вызов метода для неизвестного общего типа - PullRequest
2 голосов
/ 17 декабря 2010

У меня есть базовый класс "ManagerBase", который имеет универсальную статическую функцию "GetManager. Есть два класса, которые наследуются от ManagerBase (" ManagerSomething1 "и" ManagerSomething2 "). Каждый класс имеет статическую функцию" GetManager ", которая возвращаетунаследованный тип ManagerBase. Как бы я вызвал GetManager, не зная тип?

public class ManagerBase
{
    public static T GetManager<T>(int managerID, bool withUsers, System.Func<SqlDataReader, T> del) where T : ManagerBase
    {
        T manager = del(dr);
        return manager;
    }
}

public class ManagerSomething1 : ManagerBase
{
    public static ManagerSomething1 GetManager<ManagerSomething1>(int managerID, bool withUsers)
    {
        return ManagerBase.GetManager<ManagerSomething1>(managerID, withUsers, dr => new ManagerSomething1(dr));
    }
}

public class ManagerSomething2 : ManagerBase
{
    public static ManagerSomething2 GetManager<ManagerSomething2>(int managerID, bool withUsers)
    {
        return ManagerBase.GetManager<ManagerSomething2>(managerID, withUsers, dr => new ManagerSomething2(dr));
    }
}

public static class SessionSharedHelper<T> where T : ManagerBase
{
    public static void InitializeSession(int managerID, bool withUsers)
    {
        SessionShared<T>.Manager = //I don't know how I can call ManagerBase.GetManager<T>(managerID, withUsers, ...);
    }
}

Ответы [ 4 ]

3 голосов
/ 17 декабря 2010

Вы можете изменить рефакторинг на что-то вроде этого:

    public abstract class ManagerBase
    {
        public ManagerBase() { }

        public abstract void Initialize(int managerID, bool withUsers);
    }

    public class ManagerSomething1 : ManagerBase
    {
        public ManagerSomething1() 
        { }

        public override void Initialize(int managerID, bool withUsers)
        {
        }
    }

    public class ManagerSomething2 : ManagerBase
    {
        public ManagerSomething2()
        {
        }

        public override void Initialize(int managerID, bool withUsers)
        {
            throw new NotImplementedException();
        }
    }

    public static class SessionSharedHelper<T> where T : ManagerBase, new()
    {
        public static void InitializeSession(int managerID, bool withUsers)
        {
            T manager = new T();
            manager.Initialize(managerID, withUsers);
        }
    }
2 голосов
/ 17 декабря 2010

Примерно так может работать:

MethodInfo method_info = typeof(T).GetMethod("GetManager",
   System.Reflection.BindingFlags.Static | BindingFlags.Public);
SessionShared<T>.Manager = 
   (T)method_info.Invoke(null, new object[]{managerID, withUsers, ...});
0 голосов
/ 17 декабря 2010

Что-то где-то должно знать / решать, какой тип класса Manager создать, когда вы говорите «дайте мне объект Manager * для managerID x».Итак, один вопрос, на который нужно ответить, - как принимается это решение.Определяется ли это какими-то данными, которые возвращаются с помощью средства чтения данных?

Вместо статических методов «GetManager» можно создать класс Factory или Repository менеджера, который понимает, как определить, какой тип объекта ManagerДля создания данных читателя данных.

Ниже приведен пример реализации.Идея состоит в том, что в какой-то момент на раннем этапе, например, когда ваше приложение запускается, вы создаете ManagerRepository, а затем регистрируете делегат «create» для каждого типа класса Manager, который у вас есть.Позже, когда вы запрашиваете объект Manager из ManagerRepository, он решает, какой тип класса Manager следует вернуть, и будет использовать делегата «create», который вы зарегистрировали для этого типа.

public class ManagerBase
{
}

class ManagerRepository
{
    private Dictionary<Type, Func<SqlDataReader, ManagerBase>> _ManagerCreateDelegates;

    public ManagerRepository()
    {
        _ManagerCreateDelegates = new Dictionary<Type, Func<SqlDataReader, ManagerBase>>();
    }

    public void RegisterCreate<T>(Func<SqlDataReader, ManagerBase> create)
        where T : ManagerBase
    {
        _ManagerCreateDelegates[typeof(T)] = create;
    }

    public ManagerBase GetManager(int managerID, bool withUsers)
    {
        SqlDataReader reader;
        reader = null;// TODO: obtain a data reader from somewhere...

        Type typeOfManager = this.DetermineManagerType(reader);

        Func<SqlDataReader, ManagerBase> create;
        if (_ManagerCreateDelegates.TryGetValue(typeOfManager, out create))
        {
            return create(reader);
        }
        else
        {
            throw new InvalidOperationException(string.Format("No create delegate has been registered for type [{0}].", typeOfManager.FullName));
        }
    }

    private Type DetermineManagerType(SqlDataReader reader)
    {
        // TODO: implement logic that uses the data reader to decide which type of Manager object to create
        throw new NotImplementedException();
    }
}

public class ManagerSomething1 : ManagerBase
{
    public ManagerSomething1(SqlDataReader reader)
    {
        // TODO: implement logic to construct ManagerSomething1 given a data reader
    }
}

public class ManagerSomething2 : ManagerBase
{
    public ManagerSomething2(SqlDataReader reader)
    {
        // TODO: implement logic to construct ManagerSomething1 given a data reader
    }
}

class Program
{
    static void Main(string[] args)
    {
        // create a ManagerRepository somewhere
        ManagerRepository repository = new ManagerRepository();

        // register create delegates for each type of Manager
        repository.RegisterCreate<ManagerSomething1>(dr => new ManagerSomething1(dr));
        repository.RegisterCreate<ManagerSomething2>(dr => new ManagerSomething2(dr));

        // use the repository
        int managerID = 5;
        bool withUsers = false;
        ManagerBase manager = repository.GetManager(managerID, withUsers);
    }
}
0 голосов
/ 17 декабря 2010

Я думаю, что вы пытаетесь реализовать какой-то шаблон фабрики внутри классов, которые вы пытаетесь построить.

Вы можете пойти двумя путями: иметь отдельный класс, который знает, как создавать Manager1 и Manager2, или базовый класс, который знает, как создать каждого из его дочерних элементов. В любом случае, нужно что-то знать о каждом из подклассов. Я не думаю, что есть хороший способ сделать то, что у вас есть, без размышлений.

Я бы, наверное, реализовал нечто подобное в отдельном классе фабрики.

public static T GetManager<T>(int managerID, bool withUsers) where T : ManagerBase
{
    if (typeof(T) == typeof(Manager1))
    {
        return new Manager1(managerID, withUsers) as T;
    }
    if (typeof(T) == typeof(Manager2))
    {
        return new Manager2(managerID, withUsers) as T;
    }

    throw new ArgumentException();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...