Обобщенные структуры

84

Подобно классам, структуры также могут быть обобщенными. Они очень похожи на обобщенные классы, за исключением возможности наследования. В этой статье рассматривается обобщенная структура Nullable<T>, которая определена в .NET Framework. Итак, примером обобщенный структуры в .NET Framework является Nullable<T>.

Число в базе данных и число в языке программирования имеют важное отличие в своих характеристиках, поскольку число в базе данных может быть null. Число в C# не может быть null. Проблема существует не только с базами данных, но также с отображением данных XML на типы .NET.

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

За счет использования структуры Nullable<T> эта проблема может быть легко решена. Ниже показан фрагмент кода с упрощенной версией определения типа Nullable<T>. Структура Nullable<T> определяет ограничение, которое состоит в том, что обобщенный тип T должен быть структурой. С классами в качестве обобщенных типов преимущество минимальных накладных расходов исчезло бы, и поскольку объекты классов все равно могут быть null, то в использовании класса с типом Nullable<T> смысла нет.

Единственное дополнение к типу Т, определенное в Nullable<T>, это поле типа bool hasValue, которое определяет, установлено значение или же оно равно null. Помимо этого, обобщенная структура определяет доступные только для чтения свойства HasValue и Value, а также перегрузки некоторых операций. Перегрузка операции приведения Nullable<T> к T определена явно, так как она может генерировать исключение в случае, если hasValue равно false. Перегрузка операции для приведения к Nullable<T> определена неявно, потому что она всегда успешна:

using System;

namespace ConsoleApplication1
{
    public struct Nullable<T>
       where T : struct
    {
        public Nullable(T value)
        {
            this.hasValue = true;
            this.value = value;
        }
        private bool hasValue;
        public bool HasValue
        {
            get
            {
                return hasValue;
            }
        }
        private T value;
        public T Value
        {
            get
            {
                if (!hasValue)
                {
                    throw new InvalidOperationException("no value");
                }
                return value;
            }
        }
        public static explicit operator T(Nullable<T> value)
        {
            return value.Value;
        }

        public static implicit operator Nullable<T>(T value)
        {
            return new Nullable<T>(value);
        }

        public override string ToString()
        {
            if (!HasValue)
                return String.Empty;
            return this.value.ToString();
        }
    }

    class Program
    {
        static void Main()
        {
            Nullable<int> x;
            x = 4;

            Console.ReadLine();
        }
    }
}

В этом примере экземпляр Nullable<T> создан как Nullable<int>. Переменная х теперь может быть использована как int, т.е. ей можно присваивать значения и применять в операциях для выполнения некоторых вычислений. Такое поведение стало возможным благодаря операциям приведения типа Nullable<T>. Однако х также может быть null. Свойства HasValue и Value типа Nullable могут проверять, есть ли значение, и обращаться к нему.

Пройди тесты
Лучший чат для C# программистов