2009-05-12 3 views
4

Я написал простой абстрактный общий класс в C# (.NET 2.0), и я предпочитаю ограничивать его только ссылочными типами, поэтому я могу указать значение null. Тем не менее, я также хочу использовать такие типы, как 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 и null по-разному. По этой причине значение по умолчанию (T) не будет работать.

ответ

2

Вы должны были бы два класса

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> 
+0

Это может действительно работать, если оба они сходят с общего неродного предка (Поле) ... –

+0

@ C. Росс - Да, это может сработать. Я использовал не общий базовый класс, чтобы включить ветвление наследования раньше. – stevehipwell

0

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

public abstract Field<T> 
    where T : class 
{ 

} 

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

+0

Это не позволит использовать длинные или десятичные знаки, как указано в вопрос. Пожалуйста, прочитайте перед публикацией. –

+0

@C. Росс - я читал перед публикацией. Если вы перечитаете мой ответ, вы заметите, что я указываю, что единственный способ вернуть _null_ из общего метода, который возвращает значение, являющееся типом одного из общих аргументов, состоит в том, чтобы ограничить этот аргумент «классом». –

3

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

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

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

protected object _Value; 

А потом коробчатую распаковывать коробку для распаковывать.

+0

Хороший ответ, но, к сожалению, мне нужно представлять 0 и «Нет значения» отдельно. –

0

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

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

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

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

1

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

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

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

Смежные вопросы