Позначити параметри як НІЛИВО в C # /. NET?


98

Чи існує простий атрибут або контракт даних, який я можу призначити параметру функції, який запобігає nullпередачі в C # /. NET? В ідеалі це також перевіряє під час компіляції, щоб переконатися, що літерал nullніде не використовується для нього та під час виконання ArgumentNullException.

В даний час я пишу щось на кшталт ...

if (null == arg)
  throw new ArgumentNullException("arg");

... для кожного аргументу, якого, як я очікую, не буде null.

У тій же примітці, чи є протилежне тому, Nullable<>що наступне не вдалося б:

NonNullable<string> s = null; // throw some kind of exception

7
В останніх версіях C # ефективніше працює "nameof ()": викиньте новий ArgumentNullException (nameof (arg)); Таким чином, якщо ви переробляєте ім'я, якщо він
пульсує

C # 8 тепер підтримує типові посилальні типи, що дозволяють обнулювати, що є варіантом компілятора, який ви можете увімкнути у своєму проекті. docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Пол Стеглер

Відповіді:


69

На жаль, під час компіляції нічого не доступно.

У мене є трохи хакерське рішення, яке я нещодавно розмістив у своєму блозі, яке використовує нову структуру та перетворення.

У .NET 4.0 із матеріалами Code Contracts життя буде набагато приємнішим. Все одно було б непогано мати фактичний синтаксис мови та підтримку щодо ненульованості, але контракти коду дуже допоможуть.

У мене також є метод розширення в MiscUtil, який називається ThrowIfNull, що робить його дещо простішим.

Останній момент - будь-яка причина для використання " if (null == arg)" замість " if (arg == null)"? Мені легше читати останнє, і проблема, яку перший вирішує на C, не стосується C #.


2
> На жаль, під час компіляції нічого не доступно. > ...> Все одно було б непогано мати фактичний синтаксис мови та підтримку щодо ненульованості, я погоджуюсь. Я також хотів би бачити помилки під час компіляції.
AndrewJacksonZA

2
@Jon, чи не працює "if (arg = null)", якщо трапляється неявний привід для bool? Я визнаю, що це може здатися збоченим, але воно все-таки компілюється ...
Томас С. Тріас,

11
@ ThomasS.Trias: Так, у цьому неймовірно незрозумілому кейсовому випадку, в поєднанні з помилкою друку та нестачею тестів навколо цього коду, у вас виникла б проблема. На той момент я думаю, що у вас є більші проблеми :)
Джон Скіт,

4
@Jon: Звичайно. Думаю, я відмовлюсь від своїх улюблених умов Йоди. :-)
Томас С. Тріас,

1
@SebastianMach: Я не вважаю, що причина в цьому if (null == x)полягає в різниці між природними мовами. Я вважаю, що насправді йдеться про застарілий стиль, який просто поширюється на прикладах тощо
Джон Скіт,

22

Я знаю, що я неймовірно запізнився на це питання, але я відчуваю, що відповідь стане актуальною, оскільки остання основна ітерація C # наблизиться до випуску, а потім вийде. У C # 8.0 відбудуться серйозні зміни, C # прийме на себе все типи вважаються не нульовими.

За словами Мадса Торгерсена:

Проблема в тому, що нульові посилання настільки корисні. У C # вони є значенням за замовчуванням для кожного типу посилання. Яким ще було б значення за замовчуванням? Яке ще значення мала б змінна, поки ви не вирішите, що ще їй призначити? Яким ще значенням ми могли б прокласти щойно виділений масив посилань, доки ви не обійдетесь заповнити його?

Крім того, іноді нуль є розумним значенням сам по собі. Іноді ви хочете представити той факт, що, скажімо, поле не має значення. Це нормально передавати "нічого" для параметра. Однак акцент робиться на інколи. І в цьому полягає інша частина проблеми: такі мови, як C #, не дозволяють вам виражати, чи є нуль тут гарною ідеєю чи ні.

Отже, резолюція, викладена Мадсом, така:

  1. Ми вважаємо, що частіше хочеться, щоб посилання не було нульовим. Типи посилань, що підлягають нульовому використанню, були б рідкіснішими (хоча ми не маємо хороших даних, щоб сказати нам, скільки), тому саме вони потребують нової анотації.

  2. Мова вже має поняття - і синтаксис - типів значень, що мають значення. Аналогія між ними зробить мовне додавання концептуально простішим та лінгвістично простішим.

  3. Здається, правильно, що ви не повинні обтяжувати ні себе, ні споживача громіздкими нульовими значеннями, якщо тільки ви активно не вирішили, що хочете їх. Нулі, а не їх відсутність, повинні бути тим, для чого вам явно потрібно вибрати.

Приклад бажаної функції:

public class Person
{
     public string Name { get; set; } // Not Null
     public string? Address { get; set; } // May be Null
}

Попередній перегляд доступний для Visual Studio 2017, попередній перегляд 15.5.4+.


1
Хто-небудь знає, чи це коли-небудь стало частиною C # 8.0?
TroySteven

@TroySteven Так, це потрібно зробити, хоча вам доведеться увімкнути його, хоча через налаштування у Visual Studio.
Грег

@TroySteven Ось документація до нього. docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Грег

Приємно, тому, схоже, вам потрібно ввімкнути його до того, як він почне працювати, це добре для зворотної сумісності.
TroySteven

15

Я знаю, що це ДУЖЕ старе запитання, але тут цього бракувало:

Якщо ви використовуєте ReSharper / Rider, ви можете використовувати Annotated Framework .

Редагувати : Я щойно отримав випадковий -1 для цієї відповіді. Добре. Тільки майте на увазі, що він все ще діє, хоча це вже не рекомендований підхід для проектів C # 8.0 + (щоб зрозуміти, чому, див . Відповідь Грега ).


1
Цікаво, що за замовчуванням у вашій скомпільованій програмі не буде посилання на JetBrains.Annotations.dll, тому вам не доведеться розповсюджувати її разом із додатком: Як використовувати анотації JetBrains для покращення перевірок ReSharper
ReSharper Lu55, 02

9

Перевірте валідатори в бібліотеці підприємства. Ви можете зробити щось на зразок:

private MyType _someVariable = TenantType.None;
[NotNullValidator(MessageTemplate = "Some Variable can not be empty")]
public MyType SomeVariable {
    get {
        return _someVariable;
    }
    set {
        _someVariable = value;
    }
}

Потім у своєму коді, коли ви хочете перевірити його:

Microsoft.Practices.EnterpriseLibrary.Validation.Validator myValidator = ValidationFactory.CreateValidator<MyClass>();

ValidationResults vrInfo = InternalValidator.Validate(myObject);

0

не найкрасивіша, але:

public static bool ContainsNullParameters(object[] methodParams)
{
     return (from o in methodParams where o == null).Count() > 0;
}

Ви також можете отримати більше креативу в методі ContainsNullParameters:

public static bool ContainsNullParameters(Dictionary<string, object> methodParams, out ArgumentNullException containsNullParameters)
       {
            var nullParams = from o in methodParams
                             where o.Value == null
                             select o;

            bool paramsNull = nullParams.Count() > 0;


            if (paramsNull)
            {
                StringBuilder sb = new StringBuilder();
                foreach (var param in nullParams)
                    sb.Append(param.Key + " is null. ");

                containsNullParameters = new ArgumentNullException(sb.ToString());
            }
            else
                containsNullParameters = null;

            return paramsNull;
        }

звичайно, ви могли б використати перехоплювач або відображення, але за ними легко слідувати / використовувати з невеликими накладними витратами


3
Дуже погана практика використання таких запитів: return (from o in methodParams where o == null).Count() > 0; Використання: return methodParams.Any(o=>o==null);у великих колекціях це буде набагато швидше
Yavanosta

Навіщо випускати виняток? Чому б просто не кинути його?
Martin Capodici

-4

Гаразд, ця відповідь трохи запізнилася, але ось як я її вирішую:

public static string Default(this string x)
{
    return x ?? "";
}

Використовуйте цей метод розширення, тоді ви можете розглядати нульовий і порожній рядок як одне і те ж.

Напр

if (model.Day.Default() == "")
{
    //.. Do something to handle no Day ..
}

Не ідеально. Я знаю, оскільки ви повинні пам’ятати про те, щоб називати дефолт скрізь, але це одне з рішень.


5
як це краще / простіше, ніж (x == null) перевірки? або функція String.IsNotNullOrEmpty.
Батавія

Не набагато краще, ніж string.IsNotNullOrEmptyнасправді, крім цукру, якщо справа, про яку ви дбаєте, знаходиться зліва. Може подаватися в інші функції (довжина, об'єднання) тощо. Значно краще.
Мартін Каподічі

@MartinCapodici Крім того, це створює ілюзію, що ви можете безпечно викликати метод instance ( Default()) на null ( model.Day). Я знаю, що методи розширення не перевіряються на нуль, але мої очі не знають, і вони вже уявили собі ?код: model?.Day?.Default():)
Alb,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.