从不装箱的情况下实现结构调用 C# 接口默认方法
Calling C# interface default method from implementing struct without boxing
我唯一能想到的是如下,这与理想相去甚远:
interface IBar {
void Foo() => Console.WriteLine("Hello from interface!");
}
struct Baz : IBar {
// compiler error
void Test1() => this.Foo();
// IIRC this will box
void Test2() => ((IBar)this).Foo();
// this shouldn't box but is pretty complicated just to call a method
void Test3() {
impl(ref this);
void impl<T>(ref T self) where T : IBar
=> self.Foo();
}
}
有没有更直接的方法来做到这一点?
(相关以及我是如何得到这个问题的:)
我还没有为 C# 8.0 做好准备,所以我不确定这是否可行,但这里有一个您可以尝试的想法:
struct Baz : IBar
{
public void CallFoo()
{
this.AsBar().Foo();
}
public IBar AsBar()
{
return this;
}
}
我认为没有任何分配。 This answer to this possibly duplicate question explains that the JIT compiler can avoid boxing in many cases, including explicit interface implementation calls. Andy Ayers from the JIT team verified this in a comment and provided a link 到实现这个的 PR。
我修改了那个答案中的代码:
using System;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace DIMtest
{
interface IBar {
int Foo(int i) => i;
}
struct Baz : IBar {
//Does this box?
public int Test2(int i) => ((IBar)this).Foo(i);
}
[MemoryDiagnoser, CoreJob,MarkdownExporter]
public class Program
{
public static void Main() => BenchmarkRunner.Run<Program>();
[Benchmark]
public int ViaDIMCast()
{
int sum = 0;
for (int i = 0; i < 1000; i++)
{
sum += (new Baz().Test2(i));
}
return sum;
}
}
}
结果未显示任何分配:
BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18956
Intel Core i7-3770 CPU 3.40GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.0.100-preview9-014004
[Host] : .NET Core 3.0.0-preview9-19423-09 (CoreCLR 4.700.19.42102, CoreFX 4.700.19.42104), 64bit RyuJIT
Core : .NET Core 3.0.0-preview9-19423-09 (CoreCLR 4.700.19.42102, CoreFX 4.700.19.42104), 64bit RyuJIT
Job=Core Runtime=Core
| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|----------- |---------:|---------:|---------:|------:|------:|------:|----------:|
| ViaDIMCast | 618.5 ns | 12.05 ns | 13.40 ns | - | - | - | - |
我将 return 类型更改为 int 就像链接的答案一样,以确保该方法不会被优化掉
我唯一能想到的是如下,这与理想相去甚远:
interface IBar {
void Foo() => Console.WriteLine("Hello from interface!");
}
struct Baz : IBar {
// compiler error
void Test1() => this.Foo();
// IIRC this will box
void Test2() => ((IBar)this).Foo();
// this shouldn't box but is pretty complicated just to call a method
void Test3() {
impl(ref this);
void impl<T>(ref T self) where T : IBar
=> self.Foo();
}
}
有没有更直接的方法来做到这一点?
(相关以及我是如何得到这个问题的:
我还没有为 C# 8.0 做好准备,所以我不确定这是否可行,但这里有一个您可以尝试的想法:
struct Baz : IBar
{
public void CallFoo()
{
this.AsBar().Foo();
}
public IBar AsBar()
{
return this;
}
}
我认为没有任何分配。 This answer to this possibly duplicate question explains that the JIT compiler can avoid boxing in many cases, including explicit interface implementation calls. Andy Ayers from the JIT team verified this in a comment and provided a link 到实现这个的 PR。
我修改了那个答案中的代码:
using System;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace DIMtest
{
interface IBar {
int Foo(int i) => i;
}
struct Baz : IBar {
//Does this box?
public int Test2(int i) => ((IBar)this).Foo(i);
}
[MemoryDiagnoser, CoreJob,MarkdownExporter]
public class Program
{
public static void Main() => BenchmarkRunner.Run<Program>();
[Benchmark]
public int ViaDIMCast()
{
int sum = 0;
for (int i = 0; i < 1000; i++)
{
sum += (new Baz().Test2(i));
}
return sum;
}
}
}
结果未显示任何分配:
BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18956
Intel Core i7-3770 CPU 3.40GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.0.100-preview9-014004
[Host] : .NET Core 3.0.0-preview9-19423-09 (CoreCLR 4.700.19.42102, CoreFX 4.700.19.42104), 64bit RyuJIT
Core : .NET Core 3.0.0-preview9-19423-09 (CoreCLR 4.700.19.42102, CoreFX 4.700.19.42104), 64bit RyuJIT
Job=Core Runtime=Core
| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|----------- |---------:|---------:|---------:|------:|------:|------:|----------:|
| ViaDIMCast | 618.5 ns | 12.05 ns | 13.40 ns | - | - | - | - |
我将 return 类型更改为 int 就像链接的答案一样,以确保该方法不会被优化掉