Универсальный класс C #, использующий ссылочные типы и типы значений, допускающие значение NULL - PullRequest
3 голосов
/ 20 сентября 2011

У меня интересная проблема.Я хотел бы создать универсальный класс, который может работать как с ссылочными типами, так и с типами Nullable<T>.В основном я хочу что-то вроде:

public class ClassWithNull<T>
{
    public T varName = null;
}

Теперь это, конечно, не компилируется, потому что не всем типам может быть присвоено значение NULL, а именно типы значений, не допускающие значения NULL.Но проблема в том, что Nullable<T> является типом значения, поэтому простое добавление where T : class мне не поможет.Мой generics-foo не слишком силен, но я не смог найти способа сказать, что T должен быть либо ссылочным типом, либо типом значения NULL.

Идея, которую я должен решить, состоит в том, чтобы сделать ClassWithNull<T> абстрактным классом.Затем я мог бы добавить два подкласса, один для работы со ссылочными типами, а другой - для типов значений, допускающих значение NULL.Затем статический метод фабрики в базовом классе может использовать отражение, чтобы определить, какой подкласс должен быть создан.Примерно так:

public static ClassWithNull<T> CreateClassWithNull<T>()
{
    StackTrace st = new StackTrace();
    Type type = st.GetFrame(1).GetMethod().GetGenericArguments()[0];
    if (!type.IsValueType)
    {
        return new ClassWithReferenceType<T>();
    }
    else if (type == typeof(Nullable))
    {
        return new ClassWithNullableValueType<T>();
    }
    else
    {
        throw new Exception("Must provide nullable type.");
    }
}

Проблема здесь в том, что генерики разрешаются статически.Если ClassWithReferenceType<U> ожидает, что U будет ссылочным типом, то вызов new ClassWithReferenceType<T>() в фабричном методе является ошибкой компиляции, поскольку T не обязательно должен быть ссылочным типом.Компилятор не знает о проверке времени выполнения.

Есть идеи о том, как реализовать такую ​​вещь?

Ответы [ 2 ]

5 голосов
/ 20 сентября 2011

Как насчет:

public class ClassWithNull<T>
{
    public T varName = default(T);
}

(На самом деле, вам даже не нужно присваивание - вы можете просто оставить его в качестве значения по умолчанию при построении. Но вам может потребоваться default(T) для локальных переменных.)

Это не остановит вас от неправильного использования его с ненулевым типом значения - но достаточно ли этого?

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

public static ClassWithNull<T> CreateClassWithNullForClass<T> where T : class
{
    return new ClassWithReferenceType<T>();
}

public static ClassWithNull<T> CreateClassWithNullForNullable<T> where T : struct
{
    return new ClassWithNullableValueType<T>();
}

Поле в ClassWithNullableValueType будет Nullable<T> - T будет базовым типом.

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

0 голосов
/ 19 января 2014

Вы должны сделать это вместо:

public class ClassWithNull<T>
{
    private object varName_priv = null;

    public T varName {
        get { return (T)varName_priv; }
        set { varName_priv = value; }
    }
}

Это работает, потому что каждый не указатель типа в C # может быть преобразован в object, включая типы значений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...