Я могу вызвать свое собственное замыкание накоротко в вызове метода?

Предположим, что я хочу проверить набор объектов удостовериться, что ни один не является пустым:

if (obj != null &&
    obj.Parameters != null &&
    obj.Parameters.UserSettings != null) {

    // do something with obj.Parameters.UserSettings
}

Это - очаровательная перспектива для записи функции помощника, чтобы принять переменное количество аргументов и упростить этот вид проверки:

static bool NoNulls(params object[] objects) {
    for (int i = 0; i < objects.Length; i++)
        if (objects[i] == null) return false;

    return true;
}

Затем вышеупомянутый код мог стать:

if (NoNulls(obj, obj.Parameters, obj.Parameters.UserSettings)) {
    // do something
}

Правильно? Неправильно. Если obj является пустым, затем я получу a NullReferenceException когда я пытаюсь передать obj.Parameters кому: NoNulls.

Таким образом, вышеупомянутый подход ясно дезинформирован. Но if оператор с помощью && оператор работает просто великолепно, так как он закорачивается. Так: там какой-либо путь состоит в том, чтобы сделать метод закороченным, так, чтобы его аргументы не были оценены, пока явно не ссылается в рамках метода?

5
задан Dan Tao 16 December 2009 в 20:40
поделиться

3 ответа

Что ж, это некрасиво, но ...

static bool NoNulls(params Func<object>[] funcs) {
    for (int i = 0; i < funcs.Length; i++)
        if (funcs[i]() == null) return false;

    return true;
}

Затем вызовите это с помощью:

if (NoNulls(() => obj,
            () => obj.Parameters,
            () => obj.Parameters.UserSettings)) {
    // do something
}

В основном вы предоставляете делегатов для ленивой оценки значений, а не самих значений (как оценка именно эти значения вызывают исключение).

Я не говорю, что это красиво , но это как вариант ...

РЕДАКТИРОВАТЬ: Это на самом деле (и случайно) доходит до сердца о том, что было нужно Дэну, я думаю. Все аргументы метода оцениваются перед выполнением самого метода. Эффективное использование делегатов позволяет отложить эту оценку до тех пор, пока методу не потребуется вызвать делегат для получения значения.

9
ответ дан 13 December 2019 в 22:09
поделиться

Вы можете использовать отражения, если не против потери статической безопасности типов. Я возражаю, поэтому я просто использую вашу первую закорачивающую конструкцию. Было бы приветствоваться такая возможность, как упомянул Эрик :)

Я думал об этой проблеме несколько раз. В Lisp есть макросы, которые решают проблему так, как вы упомянули, поскольку они позволяют вам настраивать вашу оценку.

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

] Редактировать: (Ответы не позволяют мне вставлять блоки кода, поэтому редактирую свой пост)

Ой, не успеваю. Извините за это :)

Вы можете использовать отражения для поиска и оценки члена или свойства через строку. Один из моих друзей написал класс с синтаксисом вроде:

new ReflectionHelper(obj)["Parameters"]["UserSettings"]

Он работал через цепочку методов, возвращает ReflectionHelper на каждом уровне. Я знаю, что в этом примере проблема NullReferenceException. Я просто хотел продемонстрировать, как оценку можно отложить до времени исполнения.

Пример, который немного ближе к полезности:

public class Something
{
  public static object ResultOrDefault(object baseObject, params string[] chainedFields)
  {
    // ...
  }
}

Опять же, этот синтаксис воняет. Но это демонстрирует использование строк + отражений, чтобы отложить оценку до времени выполнения.

0
ответ дан 13 December 2019 в 22:09
поделиться

Вы могли бы написать функцию, которая принимает дерево выражений и преобразует это tree в форму, которая будет проверять наличие нулей и возвращать Func , которое можно безопасно оценить, чтобы определить, есть ли нуль.

Я подозреваю, что, хотя полученный код может быть крутым, это сбивало бы с толку и было бы гораздо менее производительно, чем просто написать кучу короткозамкнутых a! = null && ab! = null ... чеки. По факту, это, вероятно, будет менее производительно, чем просто проверка всех значений и обнаружение NullReferenceException (не то, чтобы я выступал за обработку исключений как поток механизма управления).

Сигнатура для такой функции будет чем-то например:

public static Func<bool> NoNulls( Expression<Func<object>> expr )

и его использование будет выглядеть примерно так:

NoNulls( () => new { a = obj, 
                     b = obj.Parameters, 
                     c = obj.Parameters.UserSettings } )();

Если у меня будет немного свободного времени, я напишу функцию, которая выполняет именно такое преобразование дерева выражений, и обновлю свой пост. Однако я уверен, что Джон Скит или Марк Грейвелл могли бы написать такую ​​функцию с одним закрытым глазом и одной рукой за спиной.

Мне также хотелось бы, чтобы в C # реализован оператор .? , который Эрик намекает на. Как сказал бы другой Эрик (Картман), это «надерет задницу».

Использование s будет выглядеть примерно так:

NoNulls( () => new { a = obj, 
                     b = obj.Parameters, 
                     c = obj.Parameters.UserSettings } )();

Если у меня будет немного свободного времени, я напишу функцию, которая выполняет именно такое преобразование дерева выражений, и обновлю свой пост. Однако я уверен, что Джон Скит или Марк Грейвелл могли бы написать такую ​​функцию с одним закрытым глазом и одной рукой за спиной.

Мне также хотелось бы, чтобы в C # реализован оператор .? , который Эрик намекает на. Как сказал бы другой Эрик (Картман), это «надерет задницу».

Использование s будет выглядеть примерно так:

NoNulls( () => new { a = obj, 
                     b = obj.Parameters, 
                     c = obj.Parameters.UserSettings } )();

Если у меня будет немного свободного времени, я напишу функцию, которая выполняет именно такое преобразование дерева выражений, и обновлю свой пост. Однако я уверен, что Джон Скит или Марк Грейвелл могли бы написать такую ​​функцию с одним закрытым глазом и одной рукой за спиной.

Мне также хотелось бы, чтобы в C # реализован оператор .? , который Эрик намекает на. Как сказал бы другой Эрик (Картман), это «надерет задницу».

1
ответ дан 13 December 2019 в 22:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: