Tutorial :What types to use for boxing in generics



Question:

I've written a simple abstract generic class in C# (.NET 2.0) and I preferably want to limit it to only reference types, so I can indicate no value by a null. However, I also want to use types such as long and decimal, why don't allow null (being structs after all). I considered making the class

public abstract Field<Nullable<T>>  {    }  

But that would prevent my use of the string type, which is a class. How can I box up my decimals and longs so I can use them in this generic.

 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;          }      }    }  

Please note that I need to represent numeric 0 and null differently. For this reason default(T) will not work.


Solution:1

You would need two classes

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

The first class would use

T  

While the second class would use

Nullable<T>  


Solution:2

The whole point of generics (among others) is to avoid boxing. See this:

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

If you need to distinguish between "0" and "not set", object is your only way out:

protected object _Value;  

And then box-unbox-box-unbox.


Solution:3

I'm not sure if you can constrain the generic parameters appropriately at compile time, but you could add in some runtime checks to only allow reference types along with the desired subset of nullable value types:

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;      }  }  

(In reality, you'd probably want to put your type checks in a constructor rather than the Value setter.)


Solution:4

Try this:

public abstract Field<T>      where T : class  {    }  

This will restrict the generic type parameter to be a reference type. This is the only way that you will be able to return null from this method. Yes, this will prevent you from passing value types to the method.


Solution:5

It's a good question. I wish this were possible:

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

and the compiler would choose the correct generic class depending on whether the constraints are satisfied.

Unfortunately, it doesn't work, which can cause problems with automatic source code generation.


Solution:6

I would suggest that you create a class Holder<T> with an unconstrained type parameter T, which exposes a field of type T; it would have both a default constructor, and a single-argument constructor of type T. Additionally, you might want to define an interface IReadableHolder<T>, which would be implemented by Holder<T>, but which could also be implemented by any class types you define (an instance of class Foo would implement IReadableHolder<Foo> by having its Value property return itself, thus allowing a routine expecting an IReadableHolder<Foo> to accept either a Holder<Foo> or a foo itself. Too bad there's no way to define an "extension interface" to add an implementation of IHolder<String> to class String.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »