Pergunta

I am writing a C# program that does calculations in several coordinate systems, and transforms between them. To prevent mix-ups I would like to use a separate, static, value-type for each kind of coordinate. eg:

struct FooSpaceCoords {
    double x, y, z;
}

struct BarSpaceCoords {
    double x, y, z;
}

Now a convenient and type-safe matrix class would be nice. But the following cannot work:

 public class MatrixTransform<To, From> where To : struct, From : struct 
 {
    .... some implementation that requires .x, .y and .z ....
 }

This fails because compiler can't know that To and From have members .x, .y & .z.

I could define an IHaveXYZ interface, but it seems to me that that will result in a lot of boxing operations which defies the spirit of the whole plan (and is less effecient, if that matters).

Is there an easy way to do what I originally wanted?

Foi útil?

Solução

I could define an IHaveXYZ interface, but it seems to me that that will result in a lot of boxing operations which defies the spirit of the whole plan (and is less effecient, if that matters).

No, it won't - if you use a generic type constraint, the generated IL will not box/unbox. For example:

interface IFoo
{
    void Foo();
}

struct Bar : IFoo
{
    public void Foo()
    {
        // Do something
    }
}

class Test
{
    static void DoFoo<T>(T value) where T : IFoo
    {
        value.Foo();
    }

    static void Main()
    {
        Bar bar = new Bar();
        DoFoo(bar); // No boxing involved
    }
}

The IL for DoFoo looks like this:

.method private hidebysig static void  DoFoo<(IFoo) T>(!!T 'value') cil managed
{
  // Code size       16 (0x10)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarga.s   'value'
  IL_0003:  constrained. !!T
  IL_0009:  callvirt   instance void IFoo::Foo()
  IL_000e:  nop
  IL_000f:  ret
} // end of method Test::DoFoo

Note the "constrained" part. From ECMA-335 section I.8.2.4:

Boxing and unboxing of generic arguments adds performance overhead to a CLI implementation. The constrained. prefix can improve performance during virtual dispatch to a method defined by a value type, by avoiding boxing the value type.

It's important that you don't just refer to value as IFoo though. This line would box the value (when T is a value type):

IFoo copy = value; // Might box

So long as you stick to the generic type parameter, you should be okay.

Outras dicas

Implement a common interface for both structs and change fields to properties.

interface ISomething
{
    Double X{ get;}
    Double Y{ get;}
    Double Z{ get;}
}

then add this interface as generic constraint. You're done

Note: as @Jon pointed generic constraints wont box the valuetypes.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top