Generics constraint for nullable or reference types

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Sam Kong

    Generics constraint for nullable or reference types

    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

    #2
    Re: Generics constraint for nullable or reference types

    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<Tfor 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

    Comment

    • Andy Bates

      #3
      Re: Generics constraint for nullable or reference types

      You can constrain what types your class can be instantiated with by using
      the where clause as in:

      class MyClass<Twhere T : class

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

      - Andy

      "Sam Kong" <sam.s.kong@gma il.comwrote in message
      news:1156806402 .810545.114680@ 75g2000cwc.goog legroups.com...
      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
      >

      Comment

      • Andy Bates

        #4
        Re: Generics constraint for nullable or reference types

        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 <intmyInt... 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<Twhere T : class

        Would obviously work with both reference types and your new
        MyNameSpace.Nul lable 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.Nul lable)...).

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

        class MyRefClass<Twhe re T : class
        class MyNullableClass <Twhere T : MyNameSpace.Nul lable

        HTH

        - Andy

        "Andy Bates" <andy@ussdev.co mwrote in message
        news:unlv4ryyGH A.1300@TK2MSFTN GP05.phx.gbl...
        You can constrain what types your class can be instantiated with by using
        the where clause as in:
        >
        class MyClass<Twhere T : class
        >
        I cannot see an obvious way to constrain the type to being a Nullable type
        as int?, Nullable<intetc . all appear to be invalid in the where clause
        above!
        >
        - Andy
        >
        "Sam Kong" <sam.s.kong@gma il.comwrote in message
        news:1156806402 .810545.114680@ 75g2000cwc.goog legroups.com...
        >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
        >>
        >
        >


        Comment

        • Sam Kong

          #5
          Re: Generics constraint for nullable or reference types


          Andy Bates wrote:
          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 <intmyInt... 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<Twhere T : class
          >
          Would obviously work with both reference types and your new
          MyNameSpace.Nul lable 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.Nul lable)...).
          >
          To avoid this you would need to define two classes, one for reference types
          and the other for your Nullable type:
          >
          class MyRefClass<Twhe re T : class
          class MyNullableClass <Twhere T : MyNameSpace.Nul lable
          >
          HTH
          >
          - 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

          Comment

          • Andy Bates

            #6
            Re: Generics constraint for nullable or reference types

            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<intmb = 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<stringm b = 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

            "Sam Kong" <sam.s.kong@gma il.comwrote in message
            news:1156914370 .314789.176170@ m79g2000cwm.goo glegroups.com.. .
            >
            Andy Bates wrote:
            >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 <intmyInt... 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<Twhere T : class
            >>
            >Would obviously work with both reference types and your new
            >MyNameSpace.Nu llable 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.Nu llable)...).
            >>
            >To avoid this you would need to define two classes, one for reference
            >types
            >and the other for your Nullable type:
            >>
            >class MyRefClass<Twhe re T : class
            >class MyNullableClass <Twhere T : MyNameSpace.Nul lable
            >>
            >HTH
            >>
            >- 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
            >

            Comment

            • Sam Kong

              #7
              Re: Generics constraint for nullable or reference types

              Hi Andy,

              Andy Bates wrote:
              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<intmb = 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<stringm b = 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.
              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

              Comment

              • Andy Bates

                #8
                Re: Generics constraint for nullable or reference types

                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:



                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

                "Sam Kong" <sam.s.kong@gma il.comwrote in message
                news:1156976128 .541817.136730@ m73g2000cwd.goo glegroups.com.. .
                Hi Andy,
                >
                Andy Bates wrote:
                >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<intm b = 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.
                >
                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
                >

                Comment

                • Sam Kong

                  #9
                  Re: Generics constraint for nullable or reference types


                  Andy Bates wrote:
                  Easy to correct the compilation error, change the line:
                  >
                  t[x, y] = null; //Compilation Error!!!
                  >
                  to
                  >
                  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

                  Comment

                  Working...