Generics Questions

U

Udi

Hi All,
Two questions regarding Generics:

1. I'd like to constrain my Type parameter to be one of a preefined
group of types. For example:

class Field<T> where T: Byte, Int, String, Array<byte> {}

- Meaning T can by either Byte, Int String or Array<byte>.

Is there a way to force a type to this constraint?

2. I have a buffer that represents one of the above types.
I'd like to add a method to get the value from the buffer.
I was hoping to do something like this:

public T GetFieldValue(byte [] buffer)
{
switch(fieldType)
{
case byte:
{
return buffer[offset]; //Error can't cast
byte to T
}
case int:
{
return (T)BitConverter.ToInt32(buffer, offset); //
Error can't cast int to T
}
:
:
}
}


The problem is that the compiler can't cast from my type to T.
Any ideas on how to solve this?
(I would like to prevent the caller to GetFieldValue() from using
another switch/case statement on result type.)

Thanks!
Udi.
 
M

Marc Gravell

No; use overloads instead - i.e. have a method pair for each of the 4
types.

Marc
 
M

Marc Gravell

For info, one other approach is to pass in (or register centrally) an
interface-implementation per type - i.e. you might have an;

interface IByteConverter<T> {
T ReadFromBuffer(byte[] buffer, int offset);
int WriteToBuffer(T value, byte[] buffer. int offset);
}

You then might have 4 concrete implementations of this, each doing
their own thing. You can either pass the implementation into the
method as a parameter, or you could register them against a generic
cache class:

static ByteConverter<T> {
public T Default {get;set;}
}

and have a bit of code assign:

ByteConverter<int> = new Int32ByteConverter();
ByteConverter<string> = new StringByteConverter();

I use a similar approach in my "protobuf-net" implementation:
http://code.google.com/p/protobuf-net/source/browse#svn/trunk/protobuf-net/Serializers

(look at ISerializer and SerializerCache, etc; the rest are
differerent implementations)

Marc
 
M

Marc Gravell

and should have been:

static ByteConverter<T> {
public IByteConverter<T> Default {get;set;}
}

i.e. for any T you can try to get the converter via
ByteConverter<T>.Default; if it is null you can't hadle the type, so
throw an exception.
 
U

Udi

and should have been:

static ByteConverter<T> {
  public IByteConverter<T> Default {get;set;}

}

i.e. for any T you can try to get the converter via
ByteConverter<T>.Default; if it is null you can't hadle the type, so
throw an exception.

Thanks for the help!
Indeed I was thinking about using an interface.
In your example however, don't I end up with the same problem inside
ReadFromBuffer()?
I won't be able to cast back to my there as well will I?
What am I missing?
 
M

Marc Gravell

In your example however, don't I end up with the same problem inside
ReadFromBuffer()?
I won't be able to cast back to my there as well will I?
What am I missing?

Each implementation is specific to the type it is designed for, so
there /is/ no cast, or if there is it is a well-defined one; for
example:

public class SingleByteConverter : IByteConverter<float> {
public float ReadFromBuffer(byte[] buffer, int offset) {
return BitConverter.ToSingle(buffer, offset);
}
public int WriteToBuffer(float value, byte[] buffer, int offset) {
byte[] tmp = BitConverter.GetBytes(value);
buffer[offset++] = tmp[0];
buffer[offset++] = tmp[1];
buffer[offset++] = tmp[2];
buffer[offset] = tmp[3];
return 4; // how many bytes we wrote
}
}

So there might be 4 different classes, each just implementing
IByteConverter< something > (for a different something).
Note that it is possible for a single class to support multiple
primates using explicit interface implementation; that is what the
various Foo_Bar files are (in the pervious link).

Of course, if you simply want to provide a binary serialization for
your classes, you could consider protobuf-net ;-p

Marc
 
P

Pavel Minaev

Aside from all the good suggestions the others have already given you
(and you are probably better off using them in this specific case),
here's some info which might be useful in general.

1. I'd like to constrain my Type parameter to be one of a preefined
group of types. For example:

    class Field<T> where T: Byte, Int, String, Array<byte> {}

    - Meaning T can by either Byte, Int String or Array<byte>.

    Is there a way to force a type to this constraint?
No.

2. I have a buffer that represents one of the above types.
    I'd like to add a method to get the value from the buffer.
    I was hoping to do something like this:

   public T GetFieldValue(byte [] buffer)
   {
      switch(fieldType)
      {
          case byte:
          {
                return buffer[offset];          //Error can't cast
byte to T
          }
          case int:
          {
                 return (T)BitConverter.ToInt32(buffer,offset);   //
Error can't cast int to T
          }
          :
          :
     }
  }

   The problem is that the compiler can't cast from my type to T.
   Any ideas on how to solve this?

Yes. First of all, you can't use switch the way you used it, but you
can use if and typeof:

if (typeof(T) == typeof(byte)) { ... }
else if (typeof(T) == typeof(int)) { ... }

What's even more interesting is that, in my experience, the JIT
compiler will actually figure out that the check is always true (or
always false) for a particular T, and will optimize away all checks,
leaving only the proper branch for the type - meaning that there's no
performance penalty.

Now the second thing, the fact that you can't cast a specific type to
T (or the other way around). This is because T may be a class, and
cast operator in C# does not allow cross-casting from a class to
another class, only between interfaces - for classes, there must be a
known inheritance relationship between them. The only way to work
around this that I know of is to first cast to Object, i.e.:

return (T)(object)byte;

Unfortunately, this is not optimized away, so you get an extra type
check for reference types if you do that, and boxing/unboxing on top
of that check for value types - and that is quite expensive.
 
U

Udi

Aside from all the good suggestions the others have already given you
(and you are probably better off using them in this specific case),
here's some info which might be useful in general.

1. I'd like to constrain my Type parameter to be one of a preefined
group of types. For example:
    class Field<T> where T: Byte, Int, String, Array<byte> {}
    - Meaning T can by either Byte, Int String or Array<byte>.
    Is there a way to force a type to this constraint?
No.





2. I have a buffer that represents one of the above types.
    I'd like to add a method to get the value from the buffer.
    I was hoping to do something like this:
   public T GetFieldValue(byte [] buffer)
   {
      switch(fieldType)
      {
          case byte:
          {
                return buffer[offset];          //Error can't cast
byte to T
          }
          case int:
          {
                 return (T)BitConverter.ToInt32(buffer, offset);   //
Error can't cast int to T
          }
          :
          :
     }
  }
   The problem is that the compiler can't cast from my type to T.
   Any ideas on how to solve this?

Yes. First of all, you can't use switch the way you used it, but you
can use if and typeof:

if (typeof(T) == typeof(byte)) { ... }
else if (typeof(T) == typeof(int)) { ... }

What's even more interesting is that, in my experience, the JIT
compiler will actually figure out that the check is always true (or
always false) for a particular T, and will optimize away all checks,
leaving only the proper branch for the type - meaning that there's no
performance penalty.

Now the second thing, the fact that you can't cast a specific type to
T (or the other way around). This is because T may be a class, and
cast operator in C# does not allow cross-casting from a class to
another class, only between interfaces - for classes, there must be a
known inheritance relationship between them. The only way to work
around this that I know of is to first cast to Object, i.e.:

  return (T)(object)byte;

Unfortunately, this is not optimized away, so you get an extra type
check for reference types if you do that, and boxing/unboxing on top
of that check for value types - and that is quite expensive.- Hide quotedtext -

- Show quoted text -

Thanks for the info!
 
B

Ben Voigt [C++ MVP]

Marc said:
For info, one other approach is to pass in (or register centrally) an
interface-implementation per type - i.e. you might have an;

interface IByteConverter<T> {
T ReadFromBuffer(byte[] buffer, int offset);
int WriteToBuffer(T value, byte[] buffer. int offset);
}

You then might have 4 concrete implementations of this, each doing
their own thing. You can either pass the implementation into the
method as a parameter, or you could register them against a generic
cache class:

static ByteConverter<T> {
public T Default {get;set;}
}

and have a bit of code assign:

ByteConverter<int> = new Int32ByteConverter();
ByteConverter<string> = new StringByteConverter();

This is really good if you want to leave it open for extension by the user.

If you don't care, then... nevermind, you can't inherit a public class from
an internal abstract class due to restrictions in .NET.
 
M

Marc Gravell

If you don't care, then... nevermind, you can't inherit a public class from
an internal abstract class due to restrictions in .NET.

Even just for internal use it can be handy; it means you don't have to
keep writing switches to choose between implementations; you just use
the registered Something<T>.Handler etc...

Marc
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top