Элегантный синтаксис для передачи пар ключ -> отображений значений в функцию? - PullRequest
1 голос
/ 02 августа 2010

Я работаю над API, который будет считывать строковые значения из файла и разрешать преобразование типов. Мне также нужно предоставить способ «сопоставления» заданных строковых значений с конкретным значением нужного типа. Это будет выглядеть примерно так:

int val = myObject.getValue<int>("FieldName", { { "", 0 }, { "INF", int.MaxValue } });

Имя поля там, чтобы его можно было найти позже, если произойдет ошибка. Это важно только для этого примера, потому что функция должна принимать несвязанный первый параметр.

Однако у меня возникают проблемы при разработке элегантного синтаксиса с безопасным типом для обеспечения этой функциональности (описанное выше, основанное на инициализаторах коллекции, работает, только если я добавлю туда new FieldValueMappingCollection<int> { ... }).


Итак, варианты, которые мне известны:
myObject.getValue<int>("FieldName", new FieldValueMappingCollection<int> { { "", 0 }, { "INF", int.MaxValue } });

или

myObject.getValue<int>("FieldName", "", 0, "INF", int.MaxValue);

где getValue имеет params object[] в своей подписи.

Должен быть лучший способ сделать это, верно?

Ответы [ 6 ]

3 голосов
/ 02 августа 2010

Лучший способ сделать это - использовать Dictionary<String, Int32> или что-то еще. Настройте его один раз в своем классе, а затем просто передайте словарь. Поскольку вы создаете API, это намного более чистый и выразительный способ объявить свое намерение, чем использование кортежей. Словарь построен для этих типов операций, и ваши пользователи должны иметь возможность легко понять цель метода. Кортежи гораздо менее понятны, особенно когда вы имеете дело с простым отображением значения ключа.

Кроме того, это значительно улучшает инициализацию, если вы хотите инициализировать на лету: http://msdn.microsoft.com/en-us/library/bb531208.aspx

1 голос
/ 02 августа 2010

Как насчет передачи делегата:

myObject.GetValue<int>("FieldName", value =>
{
    if (string.IsNullOrEmpty(value))
        return 0;
    if (value == "INF")
        return int.MaxValue;
    throw new InvalidInputException();
});

Преимущества:

  • Это поддерживает гораздо более широкий диапазон возможностей, чем просто одно-одно отображение значений.
  • Если оно становится сложным, вы можете сделать его собственным методом вместо необходимости вставлять его здесь.
  • Он полностью безопасен для типов.
  • Компилятор обязывает вас возвращать значение или выдавать исключение.Вы можете объявить свой собственный InvalidInputException, чтобы сообщить GetValue<>(), что вы не можете обработать ввод.

Недостатки:

  • Подробно...
  • Если GetValue<>() вызывает делегата только тогда, когда он не может обработать сам ввод, то вы не можете использовать это для переопределения значений, которые GetValue<>() уже считает действительными, например, вы не можетесделайте так, чтобы он отклонял -1.
  • Если GetValue<>() вызывает делегата до того, как выполнит свою собственную обработку, то при каждом исключении возникает потеря производительности.
1 голос
/ 02 августа 2010

Как насчет следующего класса:

public class MyObject<T>
{

    public T GetValue(string fieldName, params MyObjectMap<T>[] mappings)
    {
       // Do whatever you need to do
    }

    public MyObjectMap<T> Map(string from, T to)
    {
        return MyObjectMap<T>.Map(from, to);
    }

}

public class MyObjectMap<T>
{

    private MyObjectMap(string from, T to) { }

    public static MyObjectMap<T> Map(string from, T to)
    {
        return new MyObjectMap<T>(from, to);
    }

}

Вы можете использовать это, как показано ниже:

private void Foo()
{
    MyObject<int> myObject = new MyObject<int>();
    myObject.GetValue("FieldName",
        myObject.Map("", 0),
        myObject.Map("INF", int.MaxValue));
}

И это полностью тип безопасности.

0 голосов
/ 02 августа 2010

Я бы подумал сделать что-то вроде примера ниже.

Плюсы:

  • простой и компактный синтаксис с использованием анонимных типов

Минусы:

  • Потенциально много размышлений
  • ключи ограничены действительными идентификаторами, то есть специальные символы не допускаются

Метод GetValue принимает 4 параметра:

  1. имя поля, которое, как я полагаю, используется для поиска соответствующего значения из файла
  2. Значение по умолчанию, если строковое значение равно ""
  3. Объект, предположительно являющийся анонимным типом, используется в качестве карты значений. Открытые свойства действуют как ключи карты, а значения этих свойств действуют как соответствующие значения.
  4. Делегат синтаксического анализа, который используется, когда правильное значение не может быть идентифицировано через карту значений

(Похоже, мне нужна строка после списка, чтобы не испортить форматирование кода)

    public T GetValue<T>(string fieldName, T @default, object valueMap, Converter<string, T> parse)
    {
        T value;
        string literalString = null; // read string from file

        if (string.IsNullOrEmpty(literalString))
            return @default;
        else
        {
            var map = ToDictionary<T>(valueMap);
            // attempt to look up the corresponding value associated with literalString
            if (map.TryGetValue(literalString, out value))
                return value;
            else
                // literalString does not match any value in the value map,
                // so parse it using the provided delegate
                return parse(literalString);
        }
    }

    public static Dictionary<string, T> ToDictionary<T>(object valueMap)
    {
        // Use reflection to read public properties and add them as keys to a
        // dictionary along with their corresponding value
        // The generic parameter enforces that all properties
        // must be of the specified type,
        // otherwise the code here can throw an exception or ignore the property
        throw new NotImplementedException();
    }


        // usage

        int field1 = myObject.GetValue("Field1", 0, new { one = 1, two = 2, TheAnswer = 42, min = int.MinValue, max = int.MaxValue, INF = int.MaxValue }, int.Parse);

        double field2 = myObject.GetValue("Field2", 0.0, new { PI = Math.PI, min = double.MinValue, max = double.MaxValue }, double.Parse);
0 голосов
/ 02 августа 2010

Я бы не отправлял информацию в качестве параметров. Вместо этого я бы использовал свободный синтаксис, что-то вроде этого:

object.With("", 0)
      .With("INF", int.MaxValue)
      .Get<int>("FieldName");

Вы можете изменить имена методов, чтобы они выглядели так, как вы хотите. Другой вариант может быть:

object.GetValueUsing("", 0)
      .AndUsing("INF", int.MaxValue)
      .Of<int>("FieldName");

Или:

object.WithParameter("", 0)
      .AndParameter("INF", int.MaxValue)
      .GetValue<int>("FieldName");

Если вы не знаете, как создать свободный интерфейс, просто поищите в Google "c # Создание свободного интерфейса", и вы получите множество образцов.

0 голосов
/ 02 августа 2010

Обычный способ сделать это:

myObject.getValue<int>("FieldName",
    Tuple.Create("", 0),
    Tuple.Create("INF", int.MaxValue));

, используя функцию, которая принимает массив params, и либо .NET-кортежи, либо ваш собственный класс "кортеж" для этих записей поля.

Боюсь, в C # нет более краткого подхода при сохранении статической типизации.Синтаксис инициализатора коллекции не позволит вам не указывать тип.

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