请参阅代码段
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();
}
}
Run Code Online (Sandbox Code Playgroud)
这是用于调用的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
Run Code Online (Sandbox Code Playgroud)
参见约束的参考
约束前缀也可用于在值类型上调用接口方法,因为可以使用MethodImpl更改实现接口方法的值类型方法.如果未使用约束前缀,则强制编译器在编译时选择要绑定到哪个值类型的方法.使用约束前缀允许MSIL在运行时绑定到实现接口方法的方法,而不是在编译时绑定.
这意味着它将调用一个包含接口方法代码f0的方法而不用装箱结构.
在C#中,如上所述GenericClass是否存在任何其他方式没有装箱的caling接口方法?
Mar*_*ell 16
这取决于...你具体说了你不想要一个通用类 ...唯一的其他选择是一种通用的方法,在非通用类.你可以让编译器发出一个呼叫的唯一的另一个时间constrained就是你打电话ToString(),GetHashCode()或者Equals()(来自object)a struct,因为那些是constrained- 如果他们struct有,override那么call; 如果它不具有的override,他们会callvirt.这就是为什么你应该总是override那3个为什么struct; p但我离题了.一个简单的例子是带有一些静态方法的实用程序类 - 扩展方法将是一个理想的示例,因为您还可以获得编译器将在公共/隐式API和扩展/显式API之间自动切换的优势,而无需您更改代码.例如,以下(显示隐式和显式实现)没有装箱,一个call和一个constrained+ callvirt,将通过callJIT实现:
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
}
}
Run Code Online (Sandbox Code Playgroud)
这是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
}
Run Code Online (Sandbox Code Playgroud)
但是,扩展方法的一个缺点是它正在堆栈上执行额外的struct(请参阅ldloc.0)副本,如果它是超大的,或者它是一个变异方法(你应该避免),这可能是一个问题无论如何).如果是这种情况,则ref参数很有用,但请注意,扩展方法不能包含ref this参数 - 因此您无法使用扩展方法执行此操作.但考虑一下:
Bar(ref expl);
Bar(ref impl);
Run Code Online (Sandbox Code Playgroud)
有:
static void Bar<T>(ref T foo) where T : IFoo
{
foo.Bar();
}
Run Code Online (Sandbox Code Playgroud)
这是:
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&)
Run Code Online (Sandbox Code Playgroud)
有:
.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
}
Run Code Online (Sandbox Code Playgroud)
仍然没有拳击,但现在我们也永远不会复制结构,即使是明确的情况.
| 归档时间: |
|
| 查看次数: |
1295 次 |
| 最近记录: |