Какие типы использовать для бокса в дженериках - PullRequest
4 голосов
/ 12 мая 2009

Я написал простой абстрактный обобщенный класс на C # (.NET 2.0), и я предпочтительно хочу ограничить его только ссылочными типами, чтобы я мог указать нулевое значение. Тем не менее, я также хочу использовать такие типы, как long и decimal, почему бы не разрешить null (будучи в конце концов структурами). Я думал сделать класс

public abstract Field<Nullable<T>>
{

}

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

 abstract class Field<T>
 {
    private int _Length = 1;
    private bool _Required = false;
    protected T _Value; //= null;

    public int Length
    {
        get { return _Length; }
        private set
        {
            if (value < 1) throw new ArgumentException("Field length must be at least one.");
            _Length = value;
        }
    }

    public bool Required
    {
        get { return _Required; }
        private set { _Required = value; }
    }

    public abstract string GetFieldValue();
    public abstract void ParseFieldValue(string s);

    public virtual T Value
    {
        get { return _Value; }
        set
        {
            if (value == null && Required)
                throw new ArgumentException("Required values cannot be null.");
            _Value = value;
        }
    }

}

Обратите внимание, что мне нужно представлять числовой 0 и ноль по-разному. По этой причине default (T) не будет работать.

Ответы [ 6 ]

3 голосов
/ 12 мая 2009

Весь смысл дженериков (среди прочих) в том, чтобы избегать бокса. См это :

private bool _Required = false;
protected T _Value = default(T);

Если вам нужно различить «0» и «не установлено», object - ваш единственный выход:

protected object _Value;

А затем box-unbox-box-unbox.

2 голосов
/ 12 мая 2009

Вам понадобятся два класса

abstract class FieldR<T> where T: class
{
  T Value { get {} set {} }
}

abstract class FieldV<T> where T: struct
{
  Nullable<T> Value { get {} set {} }
}

Первый класс будет использовать

T

В то время как второй класс будет использовать

Nullable<T>
1 голос
/ 12 мая 2009

Я не уверен, что вы можете соответствующим образом ограничить универсальные параметры во время компиляции, но вы могли бы добавить в некоторые проверки во время выполнения, чтобы разрешить только ссылочные типы вместе с желаемым подмножеством типов значений, допускающих значение NULL:

public virtual T Value
{
    get { return _Value; }
    set
    {
        Type t = typeof(T);
        if (t.IsValueType)
        {
            if (t.IsGenericType
                && (t.GetGenericTypeDefinition() == typeof(Nullable<>)))
            {
                Type u = Nullable.GetUnderlyingType(t);
                if ((u != typeof(long)) && (u != typeof(decimal)))
                {
                    throw new ArgumentException(
                        "Only long? and decimal? permitted!");
                }
            }
            else
            {
                throw new ArgumentException("Only nullable types permitted!");
            }
        }

        if ((value == null) && Required)
        {
            throw new ArgumentException("Required values cannot be null!");
        }

        _Value = value;
    }
}

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

0 голосов
/ 27 августа 2011

Я бы предложил создать класс Holder с параметром неограниченного типа T, который предоставляет поле типа T; он будет иметь как конструктор по умолчанию, так и конструктор с одним аргументом типа T. Кроме того, вы можете определить интерфейс IReadableHolder , который будет реализован Holder , но который также может быть реализован любым типы классов, которые вы определяете (экземпляр класса Foo реализует IReadableHolder , возвращая само свойство Value, что позволяет подпрограмме, ожидающей, что IReadableHolder примет либо Holder , либо сам foo. нет способа определить «интерфейс расширения» для добавления реализации IHolder в класс String.

0 голосов
/ 12 мая 2009

Хороший вопрос. Я хотел бы, чтобы это было возможно:

class MyClass<T> where T : struct {
    T? value;
    ...
}
class MyClass<T> where T : class {
    T value;
    ...
}

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

К сожалению, это не работает, что может вызвать проблемы с автоматической генерацией исходного кода.

0 голосов
/ 12 мая 2009

Попробуйте это:

public abstract Field<T>
    where T : class
{

}

Это ограничит параметр универсального типа ссылочным типом. Это единственный способ вернуть null из этого метода. Да, это предотвратит передачу типов значений в метод.

...