为什么在使用“部分”时类级别的“不安全”修饰符不一致?

时间:2018-08-20 21:56:35

标签: c# unsafe

我希望在部分类上的类级别上使用unsafe修饰符时出现某种行为,

我一直在研究一个较大的包装器,出于理智的考虑,使用partial修饰符将其拆分为多个文件。包装器大量使用unsafe指针,因此我选择仅在类级别声明它以覆盖其中的所有内容。

public static unsafe partial class Ruby
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static VALUE CLASS_OF(VALUE obj) => ((RBasic*) obj)->klass;
}

在另一个文件中:

public static unsafe partial class Ruby
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void* DATA_PTR(VALUE obj) => ((RData*) obj)->data;
}

每个部分声明都需要unsafe修饰符,以便“允许”和编译不安全的代码,这是可以理解的,我希望部分类的类声明需要完全匹配。

但是使用这种逻辑,我也可以拥有另一个不是unsafe的文件:

[SuppressUnmanagedCodeSecurity]
public static partial class Ruby
{
    [DllImport(RUBY_LIBRARY, CallingConvention = CallingConvention.Cdecl)]
    public static extern VALUE rb_ivar_get(VALUE obj, ID name);
}

在这里,我不使用unsafe修饰符,并且完全可以接受(显然,该文件中没有不安全的代码)。

我希望澄清为什么允许这样做。每个子类的类声明不应该完全匹配吗?不允许更改/排除任何其他类修饰符,例如privatepublicabstract等,因此unsafe为何还可以。我认为该行为似乎不一致。我的猜测是,编译器会以某种方式在不同的上下文中运行它,但这只是我的推测,并希望对此比我有更多知识的人能提供一些启发。

1 个答案:

答案 0 :(得分:11)

您关于需要一致性的修饰符的断言有些偏离。例如,这是完全有效的:

public abstract partial class Foo { }

partial class Foo { }

Foo将表示一个抽象类,因为其中的一部分被声明为抽象。有关部分类的类修饰符的规则,请参见规范的第10.2.2节。以下是与unsafe局部类有关的文本:

  

在部分类型声明上使用unsafe修饰符时,只有该特定部分才被视为不安全上下文(第18.1节)。

sealedabstract应用于部分类时,该类的所有部分都被视为sealedabstract。但是,不能两者兼有。

实际上,可访问性是部分上唯一的类修饰符,必须在所有部分之间保持一致,但不需要在所有部分中都设置。

  

当部分类型声明包含可访问性规范(公共,受保护,内部和私有修饰符)时,它必须与所有包含可访问性规范的其他部分一致。如果部分类型的任何部分都不包含可访问性规范,则将为该类型提供适当的默认可访问性(第3.5.1节)。

这基本上可以归结为不允许同时声明public partial class Barinternal partial class Bar,因为各部分之间的可访问性变得不一致。声明没有可访问性的其他部分将默认为已声明的可访问性,或者是规范第3.5.1节中概述的规则所指定的默认值。

注意:我引用的规范部分来自Visual Studio 2017随附的规范版本。

ECMA-334参考如下:

23.2:

  

在部分类型声明(第15.2.7节)上使用unsafe修饰符时,仅该特定部分为   被认为是不安全的环境。

15.2.2.1:

  

如果部分类型声明(第15.2.7节)包含可访问性规范(通过公众,   受保护的,内部的和私有的修饰符),则该规范应与所有其他   包括一个可访问性规范。如果部分类型的任何部分都不包含可访问性规范,则   类型提供适当的默认可访问性(第8.5.2节)。

15.2.2.2和15.2.2.3具有有关abstractsealed修饰符的规则。