状況によって異なります...ジェネリッククラスは必要ないと具体的に言っています...他の唯一のオプションは、非ジェネリッククラスのジェネリックメソッドです。コンパイラーに呼び出しを発行させることができる他の唯一の時間は、呼び出した場合、または(から)を呼び出した場合です。がない場合は、になります。だからこそ、あなたは常にこれらの3つを必要とします;pしかし私は逸脱します。簡単な例は、いくつかの静的メソッドを持つユーティリティクラスです-拡張constrained
ToString()
GetHashCode()
Equals()
object
struct
constrained
struct
override
call
override
callvirt
override
struct
コードを変更することなく、コンパイラーがパブリック/暗黙のAPIと拡張/明示のAPIを自動的に切り替えるという利点も得られるため、メソッドは理想的な例です。たとえば、次の例(暗黙的実装と明示的実装の両方を示しています)には、JITで実装されるcall
1つと1つのconstrained
+のボックスがありません。callvirt
call
using System;
interface IFoo
{
void Bar();
}
struct ExplicitImpl : IFoo
{
void IFoo.Bar() { Console.WriteLine("ExplicitImpl"); }
}
struct ImplicitImpl : IFoo
{
public void Bar() {Console.WriteLine("ImplicitImpl");}
}
static class FooExtensions
{
public static void Bar<T>(this T foo) where T : IFoo
{
foo.Bar();
}
}
static class Program
{
static void Main()
{
var expl = new ExplicitImpl();
expl.Bar(); // via extension method
var impl = new ImplicitImpl();
impl.Bar(); // direct
}
}
そして、これがILの重要な部分です。
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] valuetype ExplicitImpl expl,
[1] valuetype ImplicitImpl impl)
L_0000: ldloca.s expl
L_0002: initobj ExplicitImpl
L_0008: ldloc.0
L_0009: call void FooExtensions::Bar<valuetype ExplicitImpl>(!!0)
L_000e: ldloca.s impl
L_0010: initobj ImplicitImpl
L_0016: ldloca.s impl
L_0018: call instance void ImplicitImpl::Bar()
L_001d: ret
}
.method public hidebysig static void Bar<(IFoo) T>(!!T foo) cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
.maxstack 8
L_0000: ldarga.s foo
L_0002: constrained. !!T
L_0008: callvirt instance void IFoo::Bar()
L_000d: ret
}
ただし、拡張メソッドの欠点の1つは、スタック上でstruct
(を参照)の余分なコピーを実行していることです。これは、サイズが大きすぎる場合、または変更メソッドである場合(回避する必要があります)に問題になる可能性があります。とりあえず)。その場合、パラメーターは役立ちますが、拡張メソッドにパラメーターを含めることはできないことに注意してください。したがって、拡張メソッドではそれを行うことはできません。しかし、考慮してください:ldloc.0
ref
ref this
Bar(ref expl);
Bar(ref impl);
と:
static void Bar<T>(ref T foo) where T : IFoo
{
foo.Bar();
}
これは:
L_001d: ldloca.s expl
L_001f: call void Program::Bar<valuetype ExplicitImpl>(!!0&)
L_0024: ldloca.s impl
L_0026: call void Program::Bar<valuetype ImplicitImpl>(!!0&)
と:
.method private hidebysig static void Bar<(IFoo) T>(!!T& foo) cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: constrained. !!T
L_0007: callvirt instance void IFoo::Bar()
L_000c: ret
}
まだボクシングはありませんが、明示的な場合でも、構造体をコピーすることはありません。