11

接口(I)是引用类型,结构(S)是值类型。结构可以实现接口。

public interface I {}
struct S: I {}

假设有一个 S 的值作为 I 的参数传递给方法。在这种情况下,它必须被装箱。

void Method(I i) {}

void Test() {
   var s = new S();
   this.Method(s); // <---- boxing!
}

在这种情况下有没有办法避免拳击?

4

2 回答 2

24

如果您将定义更改Method为:

void Method<T>(T i) where T : I
{
}

这避免了装箱,因为在运行时 CLR 根据泛型参数的类型专门化泛型方法。引用类型都可以共享相同的实现,而结构类型都有自己的版本。这意味着所Method依赖的所有操作T都将考虑具体结构类型的大小。

但是您必须小心,因为调用定义在System.ObjectlikeEquals或上的虚拟方法GetHashCode将导致i被装箱,因为虚拟方法分派需要方法表指针(尽管 JIT 在某些情况下可能能够静态地进行分派)。但是,如果您的结构类型覆盖了所讨论的虚拟方法,则不需要进行装箱,因为调用的方法再次是静态已知的,因为结构(以及它们的成员)是密封的。

通常,您可以避免调用EqualsGetHashCode直接通过T进一步约束来实现IEquatable<T>和使用IEqualityComparer<T>进行比较,例如

void Method<T>(T i) where T : I, IEquatable<T>
{
    T other = ...
    if(i.Equals(other))    //avoids boxing
    {
    }
}
于 2013-07-14T13:40:34.233 回答
0

使用泛型:

public interface I {}
public struct S : I {}
public class Foo
{
    public static void Bar<T>(T i)
        where T : I
    {}
}
于 2013-07-14T13:40:59.987 回答