Недействительные типы

Есть ли способ создать тип с нулевым значением в С# (например, DateTime или TimeSpan).

Также существует способ (возможно, атрибут) для обеспечения того, чтобы не пустые аргументы не передавались методам и свойствам без добавления

if(arg1 == null)
{
   throw new ArgumentNullException("this attribute is null")
}

Ответ 1

DateTime и TimeSpan не являются нулевыми, поскольку они struct, а не class es.

Что касается вашего второго вопроса, то нет стандартного способа сделать это на С#. Вы можете сделать это, используя PostSharp, который представляет собой структуру AOP, или SpeС#, который представляет собой совершенно новый язык (расширение С#), которое допускает некоторое желаемое поведение.

Ответ 2

Нулевая проверка, на которую вы ссылаетесь, будет проще в .NET 4.0/С# 4.0 с помощью кодовых контрактов, что делает практически то, что вы хотите.

Структуры уже не могут быть обнулены, но не создавайте свои собственные структуры как сумасшедшие - вам редко нужны они (классы far более распространены). Не существует реальной концепции "класса, не допускающего нулевых значений"; люди предложили синтаксические изменения, например:

void Foo(string! arg1) {...}

в котором компилятор выполнил бы ненулевую проверку на arg1, но на самом деле кодовые контракты делают это и многое другое. Есть несколько вещей, которые вы можете сделать в PostSharp, но это, вероятно, не стоит того, чтобы hastle.

Еще одна мысль о классе с недействительными значениями (и одна из причин, по которым они не реализованы); что бы было default(T) для класса с непустым значением?; -p Спецификация требует, чтобы default(T) был хорошо определен...

Ответ 3

Тип, не содержащий NULL, является ValueType, другими словами, struct. Строка не может быть нулевой, поэтому примером может быть:

public struct MyStruct {}

Нет встроенного способа обеспечения того, чтобы значение null не передавалось как параметр для метода (если только тип параметра не является значением ValueType). Я видел, как люди создают методы расширения для выполнения более простых (т.е. Меньше кода) утверждений о том, является ли параметр нулевым, это может быть для вас вариантом. С другой стороны, проверка начинается с самого начала; и цель проверки очень ясна. Это может быть не так, если вы используете собственный метод проверки.

С# 4.0 добавит лучшие варианты для выполнения такого рода программирования по контракту, но пока недоступен. Как указано в другом ответе PostSharp - это вариант для того, чтобы делать то, что вы хотите. PostSharp работает, добавляя шаг после компиляции, где добавляется дополнительный код.

Есть несколько вариантов статической проверки того, может ли быть передан null. Например, ReSharper позволяет вам украсить ваши собственные параметры метода с помощью атрибута [NotNull], а ReSharper будет выдавать предупреждения во время компиляции, если он может определить, что параметр может быть нулевым. Конечно, это только предупреждает вас (потенциально) о плохой практике кодирования, это не проверка времени выполнения и не должна использоваться как таковая.

Ответ 4

Вы правы: это недостаток в С# по сравнению с С++. Это позор, потому что 95% всех параметров, которые я передаю в функции, являются ненулевыми указателями. В С++ вы можете добавить документацию, проверенную на компилятор, указав, какие указатели обеспечены, чтобы указать на что-то.

Ответ 6

Переменные Structs (value type) никогда не будут нулевыми - это объясняет ваш случай DateTime. Поэтому, если ваши параметры метода являются структурами С#, вы можете быть уверены, что они никогда не будут пустыми.
Однако, если ваши параметры метода являются ссылочными типами, они могут быть нулевыми. Я не думаю, что вы можете покончить с нулевой проверкой, как вы показали выше в этом случае.

Ответ 7

Что касается второго вопроса, вот идея, вдохновленная Nullable<T>.

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

void Foo(NotNull<string> s)
{
    var x = $"{s}";
    var i = int.Parse(s);
}

Использование NotNull<T> не ограничивается аргументами метода. И было бы неплохо, если бы в будущем у языка был какой-то синтаксический сахар, например, Foo(string! s).

public struct NotNull<T> where T : class
{
    private T valueField;

    public NotNull(T value)
    {
        this.valueField = value;
        this.CheckNotNull(value);
    }

    public T Value => this.valueField;

    public static implicit operator T(NotNull<T> t)
    {
        return t.Value;
    }

    public static implicit operator NotNull<T>(T t)
    {
        return new NotNull<T>(t);
    }

    public override bool Equals(object other)
    {
        return this.Value.Equals(other);
    }

    public override int GetHashCode()
    {
        return this.Value.GetHashCode();
    }

    public override string ToString()
    {
        return this.Value.ToString();
    }

    private void CheckNotNull(T value)
    {
        if (value == null)
        {
            throw new InvalidOperationException($"Value cannot be null");
        }
    }
}

Ответ 8

Конечно, вы можете написать свои собственные типы значений (enum и struct), которые не могут быть нулевыми (если они не имеют значения NULL).

Что касается второй части, вы можете иметь общий параметр и ограничение принимать только типы значений, что означает, что аргумент не может быть нулевым - не очень полезно, учитывая, что в большинстве случаев мы используем class.

public static void Do<T>(T arg1) where T : struct
{
    //both struct and enum goes here.
}