Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / August 2006

Tip: Looking for answers? Try searching our database.

Generics constraint for nullable or reference types

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Sam Kong - 29 Aug 2006 00:06 GMT
Hello,

I want to define a generic class which should accept only nullable
types or reference types.
What's the best way to costrain it?

---------

class MyClass<T>{
 ...
}

---------

T must be either nullable type or reference type.

int? is ok.
string is ok.
int is not ok.

Thanks in advance.

Sam
Marc Gravell - 29 Aug 2006 06:18 GMT
Well, you can't do it easily - there is no single constraint that would
help here. Is there any way you can split the code out into 2 blocks,
one for class, one for Nullable<T>? You would still need to use
Default<T> for the "null", since the (class) null-reference and
Nullable<T> "null" are IIRC quite different with the compiler making
them look the same(Nullable<T> being a struct).

Marc
Andy Bates - 29 Aug 2006 06:21 GMT
You can constrain what types your class can be instantiated with by using
the where clause as in:

class MyClass<T> where T : class

I cannot see an obvious way to constrain the type to being a Nullable type
as int?, Nullable<int> etc. all appear to be invalid in the where clause
above!

- Andy

> Hello,
>
[quoted text clipped - 19 lines]
>
> Sam
Andy Bates - 29 Aug 2006 22:13 GMT
I've done some more digging and the problem is that Nullable is actually a
struct. Accordingly templates can only be derived from interfaces,
non-sealed CLASSES and types. struct's cannot be used as base classes; full
stop...

You may be able to define your own class which implements the methods in
line with how the Nullable struct operates; it basically adds a boolean to
each type to indicate whether the value has been assigned or not... I cannot
see any reason why
this will not work but you may not be able to use the short hand form; i.e.
type? you may have to use MyType.Nullable<int> myInt... as I would assume
that the short hand form would be bound to System.Nullable (could be wrong
:).

I cannot think of any reason why this will not work but unless I actually
tried to write the code could not guarantee it would be trouble free. I also
cannot think of any reason why Nullable is implemented as a struct aside
from a value type performance angle.

If you wrote your own Nullable class then:

class MyClass<T> where T : class

Would obviously work with both reference types and your new
MyNameSpace.Nullable class (as it would be a class) the only problem would
be that calling the methods on it would need an is check (i.e. if (t is
MyNameSpace.Nullable)...).

To avoid this you would need to define two classes, one for reference types
and the other for your Nullable type:

class MyRefClass<T> where T : class
class MyNullableClass<T> where T : MyNameSpace.Nullable

HTH

- Andy

> You can constrain what types your class can be instantiated with by using
> the where clause as in:
[quoted text clipped - 30 lines]
>>
>> Sam
Sam Kong - 30 Aug 2006 06:06 GMT
> I've done some more digging and the problem is that Nullable is actually a
> struct. Accordingly templates can only be derived from interfaces,
[quoted text clipped - 33 lines]
>
> - Andy

Thank you for your kind answer.

Actually I was trying to make a generic class like this.

class Board<T>{
 T[,] cells;
 ...
}

Board instances holds some 2D array of a type.
You may think of it as Sudoku board.
But I wanted to make it more generic so that strings or numbers can be
contained (not the same time though).
So it will be like string[,] or int[,].
But for empty cells, I want to use null instead of 0.(0 doesn't always
mean empty)
So it will be string[,] or int?[,].
But I couldn't find a way to define that way.
If I don't define such constraint, I can't set null to a cell like
cells[0, 0] = null;.
The compiler would complain that cells[0,0] might be a value type.
For now, I just made it for int? only.

Thanks.

Sam
Andy Bates - 30 Aug 2006 08:14 GMT
Hi -

Okay; I'm not sure that you need to place a constraint on the generic as the
compiler will do all the work for you. Consider:

class MyBoard<T>
{
 T[,] t;

 public MyBoard(int r, int c)
 {
  this.t = new T[r, c];
 }

 public T this[int x, int y]
 {
  get { return this.t[x, y]; }
  set { this.t[x, y] = value; }
 }
}

I can use this class with any type (either value or reference) and as long
as I'm careful to copy reference values can do things such as:

MyBoard<int> mb = new MyBoard<int>(2, 2);
mb[0, 0] = 2;
mb[1, 1] = null; // *Compiler error*
int a = mb[0, 0]; // Should be 2.

MyBoard<int?> mb = new MyBoard<int?>(2, 2);
mb[0, 0] = 2;
mb[1, 1] = null;
int? a = mb[1, 1]; // Should be null.

MyBoard<string> mb = new MyBoard<string>(2, 2);
...

The indexer is the key to getting/setting items into the array as this only
works with the type the class is expecting.

- Andy

>> I've done some more digging and the problem is that Nullable is actually
>> a
[quoted text clipped - 69 lines]
>
> Sam
Sam Kong - 30 Aug 2006 23:15 GMT
Hi Andy,

> Hi -
>
[quoted text clipped - 35 lines]
> The indexer is the key to getting/setting items into the array as this only
> works with the type the class is expecting.

Yes, that works.
However, I can't assign null to the array in MyBoard class.
Let's say, you want to reset the board, you might do

public void Reset(){
 for(int x = 0; x < t.GetLength(0); x++)
   for(int y = 0; y < t.GetLength(1); y++)
     t[x, y] = null; //Compilation Error!!!
}

Well, I can reset it outside the board class.
But that's not a very good design, IMHO.

Any idea?
Thanks.

Sam
Andy Bates - 31 Aug 2006 08:12 GMT
Easy to correct the compilation error, change the line:

     t[x, y] = null; //Compilation Error!!!

to

   t[x, y] = default(T);

This will create an appropriate empty value. For reference types and
nullable types it's null, for value types it's 0 and for structs it's a copy
of the struct with each field to to 0.

Information on this can be found here:

http://msdn2.microsoft.com/en-us/library/xwth0h0d.aspx

That keeps the class nicely encapsulated and resolves the problem that you
are getting instantiating your generic class with value types and structs.
We got there in the end!

- Andy

> Hi Andy,
>
[quoted text clipped - 58 lines]
>
> Sam
Sam Kong - 31 Aug 2006 16:46 GMT
> Easy to correct the compilation error, change the line:
>
[quoted text clipped - 3 lines]
>
>     t[x, y] = default(T);

Thank you very much Andy.
Now I will modify my Board class with that.

You're really helping.
Thanks again.

Sam

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.