13

コード スニペットを参照してください

public interface I0
{
    void f0();
}
public struct S0:I0
{
    void I0.f0()
    {

    }
}
public class A<E> where E :I0
{
    public E e;
    public void call()
    {
        e.f0();
    }
}

call() の IL コードは次のとおりです。

.maxstack 8
L_0000: ldarg.0 
L_0001: ldflda !0 Temp.A`1<!E>::e
L_0006: constrained !E
L_000c: callvirt instance void Temp.I0::f0()
L_0011: ret 

制約の参照を参照してください

インターフェイス メソッドを実装する値型メソッドは MethodImpl を使用して変更できるため、制約付きプレフィックスは値型でのインターフェイス メソッドの呼び出しにも使用できます。制約付きプレフィックスが使用されていない場合、コンパイラは、コンパイル時にバインドする値型のメソッドを選択する必要があります。制約付きプレフィックスを使用すると、コンパイル時ではなく、実行時にインターフェイス メソッドを実装するメソッドに MSIL をバインドできます。

つまり、構造体をボックス化せずに、f0 のインターフェイス メソッド コードを含む 1 つのメソッドを呼び出します。

上記の C# の GenericClass のように、ボクシングせずにインターフェイス メソッドを呼び出す他の方法はありますか?

4

2 に答える 2

18

状況によって異なります...ジェネリッククラスは必要ないと具体的に言っています...他の唯一のオプションは、非ジェネリッククラスのジェネリックメソッドです。コンパイラーに呼び出しを発行させることができる他の唯一の時間は、呼び出した場合、またはから)を呼び出した場合です。がない場合は、になります。だからこそ、あなたは常にこれらの3つを必要とします;pしかし私は逸脱します。簡単な例は、いくつかの静的メソッドを持つユーティリティクラスです-拡張constrainedToString()GetHashCode()Equals()objectstructconstrainedstructoverridecalloverridecallvirtoverridestructコードを変更することなく、コンパイラーがパブリック/暗黙のAPIと拡張/明示のAPIを自動的に切り替えるという利点も得られるため、メソッドは理想的な例です。たとえば、次の例(暗黙的実装と明示的実装の両方を示しています)には、JITで実装されるcall1つと1つのconstrained+のボックスがありません。callvirtcall

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.0refref 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 
}

まだボクシングはありませんが、明示的な場合でも、構造体をコピーすることはありません。

于 2012-11-26T08:17:54.760 に答える
0

インターフェイスは参照型として扱われるため、最初に基礎となる構造体をボックス化することなく、インターフェイスによって参照される構造体でメソッドを呼び出す方法はありません。

型を強制するジェネリック メソッドを使用してインターフェイスを実装する場合、C# コンパイラは単に実際の実装の詳細を持ち上げ、したがって呼び出し規約をランタイムに持ち上げます。幸いなことに、C# コンパイラは十分に賢く、対象の型がインターフェイス X を実装しており、構造体である可能性があることを JIT コンパイラに指示できます。この情報を使用して、JIT コンパイラーは、インターフェース X によって宣言されたメソッド Y を呼び出す方法を見つけ出すことができます。

X がシールされていないクラスまたはインターフェイスである場合、JIT コンパイラが引数 X によって表される実際の型を把握する実用的な方法がないため、上記のトリックは非ジェネリック メソッド呼び出しには使用できません。したがって、渡されたインターフェイスによって表される型が非シール クラスであり、構造体とシール クラスを処理する直接メソッド呼び出しである場合、C# コンパイラがルックアップ テーブルを処理する JIT を生成する方法はありません。

ダイナミックを使用すると、理論的にはボックス化を防ぐことができますが、DLR を導入することによるパフォーマンスの低下は、まったくメリットをもたらさない可能性があります。

于 2012-11-26T06:28:46.633 に答える