Доступ к членам общих типов значений в C#

Я пишу программу на C#, которая выполняет вычисления в нескольких системах координат и выполняет преобразования между ними. Чтобы избежать путаницы, я бы хотел использовать отдельный статический тип-значение для каждого типа координат. например:

struct FooSpaceCoords {
    double x, y, z;
}

struct BarSpaceCoords {
    double x, y, z;
}

Теперь удобный и типобезопасный матричный класс был бы хорош. Но следующее не может работать:

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

Это не удается, потому что компилятор не может этого знать To а также From есть участники .x, .y & .z,

Я мог бы определить IHaveXYZ интерфейс, но мне кажется, что это приведет к большому количеству операций бокса, который бросает вызов духу всего плана (и менее эффективен, если это имеет значение).

Есть ли простой способ сделать то, что я изначально хотел?

2 ответа

Решение

Я мог бы определить интерфейс IHaveXYZ, но мне кажется, что это приведет к большому количеству операций бокса, которые бросают вызов духу всего плана (и менее эффективны, если это имеет значение).

Нет, не будет - если вы используете ограничение общего типа, сгенерированный IL не будет блокировать / распаковывать. Например:

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

IL для DoFoo выглядит так:

.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

Обратите внимание на "ограниченную" часть. Из ECMA-335 раздел I.8.2.4:

Упаковка и распаковка универсальных аргументов увеличивает производительность CLI. constrained. Префикс может повысить производительность при виртуальной диспетчеризации для метода, определенного типом значения, избегая включения в него типа значения.

Важно, чтобы вы не просто ссылались на value как IFoo хоть. Эта строка будет боксировать значение (когда T это тип значения):

IFoo copy = value; // Might box

Пока вы придерживаетесь универсального параметра типа, у вас должно быть все в порядке.

Реализуйте общий интерфейс для обеих структур и измените поля на свойства.

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

затем добавьте этот интерфейс как общее ограничение. Вы сделали

Примечание: поскольку @Jon указал общие ограничения, они не будут содержать значения типов.

Другие вопросы по тегам