Argument null validator
От: Uzzy Россия  
Дата: 26.11.09 14:05
Оценка: 113 (3) :)
Доброго времени суток.

Данный пост навеян этим сообщением и последующим обсуждением: http://rsdn.ru/forum/dotnet/3590447.1.aspx
Автор: Uzzy
Дата: 03.11.09


Там приводился сырой вариант, как избавится от строковых констант, при проверке аргументов на null, то есть избавится от хард-кодед констант. Имеем пример:

        private static void FillStringBuilderCommon(StringBuilder stringBuilder, String stringArgument)
        {
            if (stringBuilder == null)
            {
                throw new ArgumentNullException("stringBuilder");            }
            if (stringArgument == null)
            {
                throw new ArgumentNullException("stringArgument");
            }

            stringBuilder.Append(stringArgument);
        }


Выделенное было давно не по душе, и искал пути, как бы сделать прямое вытягивание имен аргументов. Первоначальную идею задал топик про удобное получение имени свойства. В результате чего было получен следующий Extension method.

    static class Extensions
    {
        public static string GetName<TResult>(this Expression<Func<TResult>> expression)
        {
            return ((MemberExpression)expression.Body).Member.Name;
        }
    }


Что привело к следующему варианту функции проверки аргумента на null

        public static void NullValidate1<TResult>(Expression<Func<TResult>> argumentExpresion)
            where TResult : class
        {
            if (argumentExpresion.Compile()() == null)
            {
                throw new ArgumentNullException(argumentExpresion.GetName());
            }
        }


Жутко медленная вещь получилась, я вам скажу, что естественно меня не утраивало ни коим образом. (на этом месте заканчивается выше указанное обсуждение). Что данная функция позвляет, кратко и понятно записать вызов валидации, например:

            Tools.NullValidate1(() => stringBuilder);


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

        [DebuggerStepThrough]
        public static void NullValidate2<TResult>(Expression<Func<TResult>> argumentExpresion, TResult argument)
            where TResult : class
        {
            if (argument == null)
            {
                throw new ArgumentNullException(argumentExpresion.GetName());
            }
        }


Получилось лучше. Стало гораздо быстрее работать, но, забегая вперед, недостаточно быстро. Вызов же стал немногим сложнее. На один аргумент.

            Tools.NullValidate1(() => stringBuilder, stringBuilder);


Как показали тесты на произволительность, потери были значительными, на порядок. Вся проблема заключалась в первом аргументе. Объяснять "почему?" думаю не надо.
В итоге пришел к заключительному виду, коим пользуюсь сейчас:

        [DebuggerStepThrough]
        public static void NullValidate<TResult>(TResult argument, Func<Expression<Func<TResult>>> nameRetreiver)
            where TResult : class
        {
            if (argument == null)
            {
                throw new ArgumentNullException(nameRetreiver().GetName());
            }
        }


Вызывается так:

            Tools.NullValidate(stringBuilder, () => (() => stringBuilder));


Таким образом тестовая функция в целом выглядит следующим образом:

        private static void FillStringBuilderExpression(StringBuilder stringBuilder, String stringArgument)
        {
            Tools.NullValidate(stringArgument, () => (() => stringArgument));
            Tools.NullValidate(stringBuilder, () => (() => stringBuilder));

            stringBuilder.Append(stringArgument);
        }


Результаты перформанс теста:

[FillStringBuilderCommon] StringBuilder length: 3000000
[FillStringBuilderCommon]: 00:00:00.0532751
[FillStringBuilderExpression2] StringBuilder length: 3000000
[FillStringBuilderExpression2]: 00:00:07.2346404
[FillStringBuilderExpression] StringBuilder length: 3000000
[FillStringBuilderExpression]: 00:00:00.1308366
Press any key to exit...



Код теста:

      static void Main(string[] args)
        {
            MeasureFunc((sb, arg) => FillStringBuilderCommon(sb, arg));

            MeasureFunc((sb, arg) => FillStringBuilderExpression2(sb, arg));
            MeasureFunc((sb, arg) => FillStringBuilderExpression(sb, arg));

            Console.WriteLine("Press any key to exit...");
            Console.Read();
        }

        private static void MeasureFunc(Expression<Action<StringBuilder, string>> functionExpression)
        {
            const uint limit = 1000000;
            string title = string.Format("[{0}]", ((MethodCallExpression)functionExpression.Body).Method.Name);

            Action<StringBuilder, string> function = functionExpression.Compile();

            StringBuilder stringBuilder = new StringBuilder();
            String stringArgument = "saa";
            
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            uint index = 0;
            while (index++ < limit)
            {
                function(stringBuilder, stringArgument);
            }

            stopwatch.Stop();

            Console.WriteLine(title + " StringBuilder length: " + stringBuilder.Length);
            Console.WriteLine(title + ": " + stopwatch.Elapsed);
        }


С уважением, Александр.
Re: Argument null validator
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 26.11.09 14:12
Оценка: 1 (1) +3
Здравствуйте, Uzzy, Вы писали:


Что-то я не догоняю..... Как я понял, конечная цель — не писать слово "stringArgument" 2 раза (примерно как сделано в C с командой препроцессора ##). В твоем решении это слово все-равно приходится писать 2 раза.

ИМХО правильный подход — это писать что-то типа "Assert.CheckNotNull(expr)" и иметь пост-билд степ, который будет эмитить в бинарник вызов
"Assert.CheckNotNull(expr, "expr", file, linenumber)"
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[2]: Argument null validator
От: Uzzy Россия  
Дата: 26.11.09 14:17
Оценка:
Здравствуйте, xvost, Вы писали:

X>Здравствуйте, Uzzy, Вы писали:



X>Что-то я не догоняю..... Как я понял, конечная цель — не писать слово "stringArgument" 2 раза (примерно как сделано в C с командой препроцессора ##). В твоем решении это слово все-равно приходится писать 2 раза.

Нет, конечная цель избавится от строковых константи и ощутимо не потерять производительность.

X>ИМХО правильный подход — это писать что-то типа "Assert.CheckNotNull(expr)" и иметь пост-билд степ, который будет эмитить в бинарник вызов

X>"Assert.CheckNotNull(expr, "expr", file, linenumber)"
Вариант. Покурю в эту сторону.
Re: Argument null validator
От: Аноним  
Дата: 26.11.09 14:29
Оценка: +1
Здравствуйте, Uzzy, Вы писали:

U>Доброго времени суток.


Что-то терзают меня смутные сомнения... Exception — это исключительная ситуация. Какая, к черту, производительность? Если аргумент null, а не должен бы, не все ли равно, как долго будет получаться имя виновного? А вот за идею внешнего метода — спасибо, буду юзать. Но и это имеет ограниченную применимость: в большинстве случаев исключение не имеет конструктора с именем переменной. ЕМНИП, ArgumentOutOfRange такой. В принципе, внешний метод может генерировать целую строку под каждый тип исключений, но все равно как-то...
Re: Argument null validator
От: Niswn  
Дата: 26.11.09 15:09
Оценка: +1
Здравствуйте, Uzzy, Вы писали:


U>Таким образом тестовая функция в целом выглядит следующим образом:


U>
U>        private static void FillStringBuilderExpression(StringBuilder stringBuilder, String stringArgument)
U>        {
U>            Tools.NullValidate(stringArgument, () => (() => stringArgument));
U>            Tools.NullValidate(stringBuilder, () => (() => stringBuilder));
U>
U>            stringBuilder.Append(stringArgument);
U>        }
U>


Ну не знаю. По моему это уже лишнее.
Минусы видны сразу:
1. Снижает читабельность кода по сравнению с обычным if + throw. К вам в проект пришел новый программист. Представьте сколько времени он будет вникать в этот проект, если всё написано в таком стиле.
2. Надо понять, что это за функция, как она вызывается, и главное при написании не запутаться в этих скобках и стрелочках.
3. Даже по вашему тесту скорость падает в 3 раза, хотя действие то примитивно (по сути одна операция всего) — проверка на null, которая при обычных обстоятельствах всегда будет false (то есть переменная не будет равна null). Вы скажете, что падение скорости несущественна, но если все примитивные действия в программе писать так сложно, то скорость работы сильно упадет.

Единственный плюс — избавление от строковой константы. Очень сомнительный плюс. Я даже не могу придумать, чем строка может повредить.

Хотя отдаю вам должное, экстешн метод написан красиво
ICQ: 326084932
Re: Argument null validator
От: samius Япония http://sams-tricks.blogspot.com
Дата: 26.11.09 15:18
Оценка:
Здравствуйте, Uzzy, Вы писали:

U>Таким образом тестовая функция в целом выглядит следующим образом:


U>
U>        private static void FillStringBuilderExpression(StringBuilder stringBuilder, String stringArgument)
U>        {
U>            Tools.NullValidate(stringArgument, () => (() => stringArgument));
U>            Tools.NullValidate(stringBuilder, () => (() => stringBuilder));
U>
U>            stringBuilder.Append(stringArgument);
U>        }
U>


Занимательная эквилибристика!
В моих проектах это практического приложения не найдет.
Re: Argument null validator
От: VladD2 Российская Империя www.nemerle.org
Дата: 26.11.09 15:26
Оценка: 22 (3)
Здравствуйте, Uzzy, Вы писали:

U>Что привело к следующему варианту функции проверки аргумента на null


U>
U>        public static void NullValidate1<TResult>(Expression<Func<TResult>> argumentExpresion)
U>            where TResult : class
U>        {
U>            if (argumentExpresion.Compile()() == null)
U>            {
U>                throw new ArgumentNullException(argumentExpresion.GetName());
U>            }
U>        }
U>


U>Жутко медленная вещь получилась, я вам скажу, что естественно меня не утраивало...


Вообще, все это от бедности. Подобные вещи решались бы элементарно и работали бы так же быстро как рукописный код если бы была возможность вмешиваться в процесс компиляции.
Вот здесь
Автор(ы): Чистяков Влад (VladD2)
Дата: 05.02.2008
В третьей части статьи о макросах Nemerle речь пойдет о разработке метаатрибутов, то есть макросов, которые выглядят как Custom-атрибуты.
рассказывается о макросе NotNull, входящего в стандартную библиотеку Nemerle, который решает эту проблему самым оптимальным образом.\
Помечаем параметр специальным атрибутом:
public class A
{
  public static Method1([NotNull] parameter : string) : void
  {
    WriteLine(parameter)
  }
}

и все! Все остальное делает макрос. При этом есть проверки времени компиляции и генерируемый код оптимален.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Argument null validator
От: Пельмешко Россия blog
Дата: 26.11.09 16:34
Оценка:
Здравствуйте, Uzzy, Вы писали:

U>Доброго времени суток.


U>Данный пост навеян этим сообщением и последующим обсуждением: http://rsdn.ru/forum/dotnet/3590447.1.aspx
Автор: Uzzy
Дата: 03.11.09


U>Там приводился сырой вариант, как избавится от строковых констант, при проверке аргументов на null, то есть избавится от хард-кодед констант. Имеем пример...


U><skipped>


U>В итоге пришел к заключительному виду, коим пользуюсь сейчас...


Поглядел на Ваш пост и подумал, а как ещё можно узнать имя передаваемого аргумента?
Бросил курсовую и написал вот такой изврат:
  static class Arg
  {
    #region Public Methods

    // Не инлайнится, сука такая >:o
    public static void NotNullRef<T>(T value) where T : class
    {
      if (value == null)
      {
        throw new ArgumentNullException(GetArgumentName());
      }
    }

    // А это инлайнится, но меньше контроля в compile-time:
    // можно valuetype передать случайно
    // или ref-аргумент (они не поддерживаются) :(
    public static void NotNull(object value)
    {
      if (value == null)
      {
        throw new ArgumentNullException(GetArgumentName());
      }
    }

    #endregion
    #region Crazy Implementation

    static readonly MethodBase NotNullInfo1 = typeof(Arg).GetMethod("NotNull");
    static readonly MethodBase NotNullInfo2 = typeof(Arg).GetMethod("NotNullRef");

    const byte Ldarg0 = 0x02;
    const byte Ldarg3 = 0x05;
    const byte LdargS = 0x0E;

    // выглядит как шутка, но всё же...
    [MethodImpl(MethodImplOptions.NoInlining)]

    private static string GetArgumentName()
    {
      var stackTrace = new StackTrace(false);

      // смотрим кто нас вызвал
      var frame = stackTrace.GetFrame(1);
      if (frame == null) return null;

      var method = frame.GetMethod();

      // проверяем, заинлайнило ли вызов NotNullRef
      if (method == NotNullInfo1 // а вот вдруг?
       || method == NotNullInfo2)
      {
        frame = stackTrace.GetFrame(2); // ниже
        if (frame == null) return null;

        method = frame.GetMethod();
      }

      // метод получили, теперь берём его внутренности :)
      byte[] il;
      try // может упасть из-за reflection permission?
      {
        var methodBody = method.GetMethodBody();
        if (methodBody == null) return null;
        il = methodBody.GetILAsByteArray();
      }
      catch (System.Security.SecurityException) { return null; }

      var offset = frame.GetILOffset();
      var parameters = method.GetParameters();

      // теперь исследуем MSIL перед вызовом NotNullRef()
      // сдвиг на 4 байта токена метода и 1 байт опкода call
      offset -= 5;

      Debug.Assert(il[offset] == 0x28); // это точно call

      offset--;
      if (offset < 0) return null; // на всякий пожарный

      string nameShort = null,
             nameLong  = null;

      // ищем короткие опкоды ldarg.x
      if (il[offset] >= Ldarg0 &&
          il[offset] <= Ldarg3)
      {
        int id = il[offset] - 2; // порядковый номер аргумента
        if (!method.IsStatic) id--; // учитываем instance-методы

        nameShort = TryGetParameterName(id, parameters);
      }

      offset--; // ещё левее
      // ищем "длинный" вариант - ldarg.S <index>
      if (offset >= 0 && il[offset] == LdargS)
      {
        int id = il[offset + 1];
        if (!method.IsStatic) id--;

        nameLong = TryGetParameterName(id, parameters);
      }

      // а теперь оцениваем что узнали
      if (nameShort != null)
      {
        if (nameLong != null)
        {
          if (nameLong == nameShort) return nameShort;

          // будем уж честными,
          // скажем что не знаем какой именно аргумент :)
          return nameLong + " or " + nameLong;
        }

        return nameShort;
      }
      
      return nameLong;
    }

    static string TryGetParameterName(int id, ParameterInfo[] parameters)
    {
      Debug.Assert(parameters != null);

      if (id < 0 || id >= parameters.Length) return null;

      var type = parameters[id].ParameterType;
      
      // оставляем только ref-типы
      if (type.IsValueType) return null;
      
      return parameters[id].Name;
    }

    #endregion
  }

Выглядит жестоко, конечно, но оно работает в 90% случаев ))))
Не работает с ref-аргументами и в случаях, когда метод с проверкой заинлайнен — .GetILAsByteArray() возвращает тело метода, в который встроен метод с проверкой

p.s. Я особо не тестировал, может упустил чего.
p.p.s. Сложно по msil перед вызовом судить что там на самом деле... может быть такая ситуация:
[какой-нить опкод][и его аргумент 0x0E][ldarg.2][вызов]
А так как 0x0E — это ещё и код опкода [ldarg.s], то для кода выше эта ситуация выглядит некорректно:
[что-то там][ldarg.s][0x04][вызов]
и вполне может выйти так, что 4ый аргумент существует и ref-типа В этом случае код вернёт ParamName = "realNullArg or someOtherRefArg".

Короче занятный костыль
Re[2]: Argument null validator
От: Пельмешко Россия blog
Дата: 26.11.09 16:55
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Вообще, все это от бедности. Подобные вещи решались бы элементарно и работали бы так же быстро как рукописный код если бы была возможность вмешиваться в процесс компиляции.

VD>Вот здесь
Автор(ы): Чистяков Влад (VladD2)
Дата: 05.02.2008
В третьей части статьи о макросах Nemerle речь пойдет о разработке метаатрибутов, то есть макросов, которые выглядят как Custom-атрибуты.
рассказывается о макросе NotNull, входящего в стандартную библиотеку Nemerle, который решает эту проблему самым оптимальным образом.\

VD>Помечаем параметр специальным атрибутом:
VD>
VD>public class A
VD>{
VD>  public static Method1([NotNull] parameter : string) : void
VD>  {
VD>    WriteLine(parameter)
VD>  }
VD>} 
VD>

VD>и все! Все остальное делает макрос. При этом есть проверки времени компиляции и генерируемый код оптимален.

Эх, круто А если Assertion-метод, который используется макросом, будет отмечен [ConditionalAttribute("NEVER")], то будет ли устранён его вызов в макросе? Даже если вызов будет устранён, то в макросе останется проверка, можно ли устранить и её (эдакий conditional-макрос)?
Re[2]: Argument null validator
От: Пельмешко Россия blog
Дата: 26.11.09 17:29
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Короче занятный костыль


Упс, инлайнинг не до конца предусмотрел:
      var method = frame.GetMethod();
      bool isInlined = true;

      // проверяем, заинлайнило ли вызов NotNullRef
      if (method == NotNullInfo1
       || method == NotNullInfo2)
      {
        frame = stackTrace.GetFrame(2); // ниже
        if (frame == null) return null;

        method = frame.GetMethod();
        isInlined = false;
      }

      ...

      if (isInlined) offset++;
      else offset -= 5;

      Debug.Assert(il[offset] == 0x28); // это точно call
Re[2]: Argument null validator
От: Jack128  
Дата: 26.11.09 18:39
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Uzzy, Вы писали:


U>>Доброго времени суток.


А>Что-то терзают меня смутные сомнения... Exception — это исключительная ситуация. Какая, к черту, производительность? Если аргумент null, а не должен бы, не все ли равно, как долго будет получаться имя виновного?


вты не понял, Tools.NullValidate1 просаживает производительность в ЛЮБОМ случае. даже если аргумент != null
Re: Argument null validator
От: _FRED_ Черногория
Дата: 26.11.09 18:59
Оценка: +2
Здравствуйте, Uzzy, Вы писали:

U>Данный пост навеян этим сообщением и последующим обсуждением: http://rsdn.ru/forum/dotnet/3590447.1.aspx
Автор: Uzzy
Дата: 03.11.09


CodeContract смотрел? Не кажется, что там выбран лучший способ решить ту же задачу?
Help will always be given at Hogwarts to those who ask for it.
Re[3]: Argument null validator
От: Аноним  
Дата: 26.11.09 19:06
Оценка: 10 (1) +2
Здравствуйте, Jack128, Вы писали:

А>>Что-то терзают меня смутные сомнения... Exception — это исключительная ситуация. Какая, к черту, производительность? Если аргумент null, а не должен бы, не все ли равно, как долго будет получаться имя виновного?


J>вты не понял, Tools.NullValidate1 просаживает производительность в ЛЮБОМ случае. даже если аргумент != null


Да, этой глупости я и не понял. Задача ставится как? Избавиться от строковых констант ("Там приводился сырой вариант, как избавится от строковых констант, при проверке аргументов на null, то есть избавится от хард-кодед констант"). Что мешает сделать хотя бы так?
        private static void FillStringBuilderCommon(StringBuilder stringBuilder, String stringArgument)
        {
            if (stringBuilder == null)
                Tools.ThrowNullArgumentException(() => stringBuilder);
            if (stringArgument == null)
                Tools.ThrowNullArgumentException(() => stringArgument);

            stringBuilder.Append(stringArgument);
        }


Проверки на null все равно не избежать, а если почки отказали, уже поздно о производительности думать.

Если хочется решить другую задачу (не писать два раза имя переменной), то для этого нужен соответствующий язык, поддерживающий макросы, как правильно указал VladD2. Хотя я предпочитаю препроцессор, генерирующий CS.
Re[3]: Argument null validator
От: VladD2 Российская Империя www.nemerle.org
Дата: 26.11.09 19:23
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>А если Assertion-метод, который используется макросом, будет отмечен [ConditionalAttribute("NEVER")], то будет ли устранён его вызов в макросе? Даже если вызов будет устранён, то в макросе останется проверка, можно ли устранить и её (эдакий conditional-макрос)?


Макрос не генерирует никаких вызовов методов. Он просто вставляет проверки, так как-будто их написали по месту.
Но так как это макрос, то конечно можно реализовать его и так чтобы он генерировал вызов метода. Тогда, если у метода задан указанный атрибут, метод вызываться не будет. Но это уже реализуется джитом.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Argument null validator
От: VladD2 Российская Империя www.nemerle.org
Дата: 26.11.09 19:26
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>(эдакий conditional-макрос)?


Макрос — это код который запускается во время компиляции. Он может все что может обычный код. В том числе из мкроса можно проверить опции компиляции или значение некоторых констант. Так что нет никаких проблем делать работу макросов зависящей от условий.

Я почти уверен, что если бы в языках дотнета исходно поддерживались макросы, то conditional-атрибутов не появилось бы.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Argument null validator
От: Пельмешко Россия blog
Дата: 26.11.09 20:01
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, Пельмешко, Вы писали:


П>>А если Assertion-метод, который используется макросом, будет отмечен [ConditionalAttribute("NEVER")], то будет ли устранён его вызов в макросе? Даже если вызов будет устранён, то в макросе останется проверка, можно ли устранить и её (эдакий conditional-макрос)?


VD>Макрос не генерирует никаких вызовов методов. Он просто вставляет проверки, так как-будто их написали по месту.


То есть Assert здесь — это не вызов, а встроенный в Nemerle оператор?
  m.Body = <[
    Assert($(p.ParsedName : name) : object != null, $(msg : string));
    $(m.Body)
  ]>;

VD>Но так как это макрос, то конечно можно реализовать его и так чтобы он генерировал вызов метода. Тогда, если у метода задан указанный атрибут, метод вызываться не будет. Но это уже реализуется джитом.

Не соглашусь, устранение [Conditional]-вызовов является чисто compile-time преобразованием... Это слишком сложное преобразование для этапа jit'а, да и так поздно его делать нету смысла, так как информации о define'ах вообще нет в сборке.

Я спросил потому что интересно, когда именно компилятор Nemerle устраняет [Conditional]-вызов, после "разворачивания" макросов?

VD>В том числе из макроса можно проверить опции компиляции или значение некоторых констант.


Это и хотел узнать, клёво
Re[2]: Argument null validator
От: Uzzy Россия  
Дата: 26.11.09 20:10
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Здравствуйте, Uzzy, Вы писали:


U>>Данный пост навеян этим сообщением и последующим обсуждением: http://rsdn.ru/forum/dotnet/3590447.1.aspx
Автор: Uzzy
Дата: 03.11.09


_FR>CodeContract смотрел? Не кажется, что там выбран лучший способ решить ту же задачу?


Не у всех имеется VS2008 Professional+ . Тем не менее, вещь интересная.
Re[4]: Argument null validator
От: Uzzy Россия  
Дата: 26.11.09 20:14
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Jack128, Вы писали:


А>>>Что-то терзают меня смутные сомнения... Exception — это исключительная ситуация. Какая, к черту, производительность? Если аргумент null, а не должен бы, не все ли равно, как долго будет получаться имя виновного?


J>>вты не понял, Tools.NullValidate1 просаживает производительность в ЛЮБОМ случае. даже если аргумент != null


А>Да, этой глупости я и не понял. Задача ставится как? Избавиться от строковых констант ("Там приводился сырой вариант, как избавится от строковых констант, при проверке аргументов на null, то есть избавится от хард-кодед констант"). Что мешает сделать хотя бы так?

А>
А>        private static void FillStringBuilderCommon(StringBuilder stringBuilder, String stringArgument)
А>        {
А>            if (stringBuilder == null)
А>                Tools.ThrowNullArgumentException(() => stringBuilder);
А>            if (stringArgument == null)
А>                Tools.ThrowNullArgumentException(() => stringArgument);

А>            stringBuilder.Append(stringArgument);
А>        }
А>

Ничего. В стартовом топике указана ссылка, где как раз такой вариант и используется.
Re[5]: Argument null validator
От: VladD2 Российская Империя www.nemerle.org
Дата: 26.11.09 20:32
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>То есть Assert здесь — это не вызов, а встроенный в Nemerle оператор?

П>
П>  m.Body = <[
П>    Assert($(p.ParsedName : name) : object != null, $(msg : string));
П>    $(m.Body)
П>  ]>;
П>


Ой. Вру. В статье использовался Trace.Assert(). Просто в реальной библиотеке используется исключение:
  [Nemerle.MacroUsage (MacroPhase.WithTypedMembers, MacroTargets.Parameter,
                       Inherited = true, AllowMultiple = false)]
  macro NotNull(_ : TypeBuilder, m : MethodBuilder, p : ParameterBuilder)
  {
    if (p.ty.CanBeNull)
    {
      def name      = <[ $(p.AsParsed().PName : name) ]>;
      def nameLoc   = p.NameLocation;
      def loc       = m.Body.Location;
      def paramName = p.Name.ToString();
      def msg       = $<#The ``NotNull'' contract of parameter ``$paramName'' has been violated. See $nameLoc.#>;
      def condition = if (p.ty.Fix().IsValueType) name
                      else                        <[ $name : object ]>;

      m.Body = <[
        when ($condition == null)
        {
          //System.Diagnostics.Debug.Fail($(msg : string));
          throw System.ArgumentNullException($(paramName : string), $(msg : string))
        }
          
        $(m.Body)
      ]>;
      
      m.Body.Location = loc;
    }
    else
      Message.Warning(p.Location, 
        $"The ``NotNull'' contract for parameter ``$(p.Name)'' has no effect. Instance of type ``$(p.ty)'' can't be null reference.");
  }


VD>>Но так как это макрос, то конечно можно реализовать его и так чтобы он генерировал вызов метода. Тогда, если у метода задан указанный атрибут, метод вызываться не будет. Но это уже реализуется джитом.


П>Не соглашусь, устранение [Conditional]-вызовов является чисто compile-time преобразованием... Это слишком сложное преобразование для этапа jit'а,


Ну, почему? Джит бы справился бы.

П>да и так поздно его делать нету смысла, так как информации о define'ах вообще нет в сборке.


А вот это — да. Тут ты прав. define-ы — это чисто компайл-тайм сущность, так что действительно ConditionalAttribute видимо обрабатывается компилятором. Не уверен, что немерловый компилятор это делает.

П>Я спросил потому что интересно, когда именно компилятор Nemerle устраняет [Conditional]-вызов, после "разворачивания" макросов?


Не факт, что он их вообще устраняет. Боюсь, что это просто не реализовано. Я думал — это делает джит. Но действительно без дефайнов это не сделать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Argument null validator
От: desco США http://v2matveev.blogspot.com
Дата: 26.11.09 21:42
Оценка: 187 (8)
Здравствуйте, Uzzy, Вы писали:

<skipped/>

можно сократить запись, если для получения имени использовать не expression trees, а анонимные типы

static class NullValidator<T>
{
    private static readonly T Default = (T) FormatterServices.GetSafeUninitializedObject(typeof (T));
    public static void Validate(T argument)
    {
        if (argument.Equals(Default))
        {
            throw new ArgumentNullException(typeof(T).GetProperties()[0].Name);
        }
    }
}

    public static void NullValidate<TResult>(TResult argument)
        where TResult : class
    {
        NullValidator<TResult>.Validate(argument);

    }
        // исходный вариант
        Tools.NullValidate(stringArgument, () => (() => stringArgument));
        Tools.NullValidate(stringBuilder, () => (() => stringBuilder));
        // вариант с анонимными типами
        Tools.NullValidate(new {stringArgument});
        Tools.NullValidate(new {stringBuilder});



A member declarator can be abbreviated to a simple name (§7.5.2) or a member access (§7.5.4). This is called a projection initializer and is shorthand for a declaration of and assignment to a property with the same name. Specifically, member declarators of the forms
identifier expr . identifier
are precisely equivalent to the following, respectively:
identifer = identifier identifier = expr . identifier

Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.