Можно ли превратить этот класс в общий? - PullRequest
2 голосов
/ 30 августа 2011

Может ли кто-нибудь «обобщить» класс, указанный внизу этого вопроса? Я хотел бы, чтобы класс был заблокирован не для делегата типа «GetStateDelegate», а для общего делегата. Это возможно?

Я пробовал пару вещей, но 1) C # не нравится объявление класса такого типа:

public class StringToMethodMapper<T> where T: System.Delegate

Это выдает «Невозможно использовать System.Delegate в качестве ограничения параметра типа»

Подсказки, помогающие мне выбрать правильный путь, приветствуются. Кроме того, если решение существует, я хочу, чтобы изменения как можно меньше затронули потребителя. Например, я бы не хотел менять вызывающий код для «Добавить (строка, делегат)».

код:

public delegate bool GetStateDelegate(object someObject);

public class StringToMethodMapper
{
    private Dictionary<string, GetStateDelegate> _methods;

    public StringToMethodMapper()
    {
        _methods = new Dictionary<string, GetStateDelegate>();
    }

    public void Add(string key, GetStateDelegate method)
    {
        _methods.Add(key, method);
    }

    internal virtual GetStateDelegate GetMethodFor(string key)
    {
        foreach (var storedKey in _methods.Keys)
        {
            if (key.ToUpper().StartsWith(storedKey.ToUpper()))
            {
                return _methods[storedKey];
            }
        }
        return null;
    }
}

Ответы [ 3 ]

1 голос
/ 30 августа 2011

С точки зрения того, чтобы сделать его родовым ... ну, это возможно. У меня есть библиотека с именем Unconstrained Melody , которая использует перезапись IL для обобщений с ограничениями делегатов - и вы можете использовать тот же перезаписывающий IL в своем коде. Это довольно уродливо, хотя. По сути, IL поддерживает ограничение, которое вы хотите, а C # - нет. Обратите внимание, что нет никаких возможных ограничений для «это должен быть тип, производный от MulticastDelegate, но не включающий MulticastDelegate сам» ... так что кто-то может создать StringToMethodMapper<MulticastDelegate>, но это довольно непривычно.

Если вы счастливы придерживаться одного вида подписи делегата (например, «всегда три параметра и возврат по пустоте»), тогда вы можете использовать подход из ответа Джорджа. Если это должно быть для любого типа делегата, то вы застряли с подходом переписывания IL или отказались от ограничения.


(Отредактировано на основе комментариев.)

С точки зрения остальной части кода, это очень медленное использование словаря. В настоящее время у вас есть O (n) поиск. Просто используйте обычный Dictionary доступ через TryGetValue, но передайте StringComparer.OrdinalIgnoreCase (или что-то подобное) в конструктор, чтобы получить регистронезависимое совпадение. По общему признанию, это не приведет к совпадению «начинается с», но ваш текущий подход не является детерминированным, так как в итоге вы можете использовать «foo» и «fo» в качестве ключей в словаре, оба из которых будут совпадать - так что вы полагаетесь на порядок, в котором итератор возвращает ключи. Не очень хорошая идея.

Если вам действительно нужно поведение StartsWith, возможно, вы захотите исследовать реализацию trie - или, если вы довольны O (N) поиском, я бы оставил List<KeyValuePair<string, TDelegate>>, чтобы сделать ясно, что вы не используете его в качестве словаря.

1 голос
/ 30 августа 2011

Возможно, что-то вроде ниже (напечатано без IDE).Очевидно, клиенту нужно будет добавить общие параметры, но я думаю, что вызов для добавления все еще работает.

public class StringToMethodMapper<T, TResult>
{
    private Dictionary<string, Func<T, TResult>> _methods;

    public StringToMethodMapper()
    {
        _methods = new Dictionary<string, Func<T, TResult>>();
    }

    public void Add(string key, Func<T, TResult> method)
    {
        _methods.Add(key, method);
    }

    internal virtual Func<T, TResult> GetMethodFor(string key)
    {
        foreach (var storedKey in _methods.Keys)
        {
            if (key.ToUpper().StartsWith(storedKey.ToUpper()))
            {
                return _methods[storedKey];
            }
        }
        return null;
    }
}
0 голосов
/ 30 августа 2011

Один из способов сделать это - поменять ваш делегат на интерфейс

public interface IGetState
{
   bool GetState(object someObject);
}

, затем вы можете использовать этот интерфейс в качестве ограничения

public class StringToMethodMapper<T> where T: IGetState
{
   ...
}

Затем вы можете реализовать интерфейс длясделать что-то конкретное, например:

public class FileSystemState : IGetState
{
   public bool GetState(object someObject)
   {
      // get state from the FS
   }
}

, а затем

var fileSystemMapper = new StringToMethodMapper<FileSystemState>();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...