Доступ к членам общих типов значений в 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 указал общие ограничения, они не будут содержать значения типов.