F#“非托管”类型约束的行为
F#支持“非托管”的类型约束。 这与“struct”约束之类的值类型约束不同。 MSDN注意到非托管约束的行为是:
提供的类型必须是非托管类型。 非托管类型是某些基本类型(sbyte,byte,char,nativeint,unativeint,float32,float,int16,uint16,int32,uint32,int64,uint64或decimal),枚举类型,nativeptr 或非通用结构,其字段都是非托管类型。
这是一个非常方便的约束类型,在进行平台调用时,我不止一次希望C#有办法做到这一点。 C#没有这个约束。 C#不支持可在CIL中指定的所有约束。 一个例子是枚举。 在C#中,你不能这样做:
public void Foo(T bar) where T:enum
但是,如果在另一个库中遇到它,C#编译器确实遵守“枚举”约束。 Jon Skeet能够用它来创建他的无约束旋律项目。
所以,我的问题是,F#的“非托管”约束是什么,可以在CIL中表示,就像一个枚举约束,只是没有暴露在C#中,或者它是纯粹由F#编译器强制实施的,就像F#支持的其他一些约束(如明确的成员约束)?
我得到了一些反馈,请注意我不太了解F#。 请编辑我的位置。 首先了解基础知识,运行时实际上并没有实现F#支持的约束。 并且支持的不仅仅是C#支持的function。 它只有4种类型的约束:
- 必须是引用类型(C#中的类约束,而不是F#中的struct)
- 必须是值类型(C#和F#中的结构约束)
- 必须有一个默认的构造函数(C#中的new()约束,F#中的new)
- 受类型约束。
然后,CLI规范就这些约束如何在特定类型参数类型上有效设置特定规则,按ValueType,Enum,Delegate,Array和任何其他任意类型进行细分。
语言设计者可以自由地使用他们的语言进行创新,只要他们遵守运行时可以支持的内容即可。 他们可以自己添加任意约束,他们有一个编译器来强制执行它们。 或者任意选择不支持运行时支持的那个,因为它不适合他们的语言设计。
只要generics类型只用于F#代码,F#扩展就可以正常工作。 所以F#编译器可以强制执行它。 但它无法通过运行时validation,如果这种类型被另一种语言使用,它根本不会产生任何影响。 约束使用F#特定属性(Core.CompilationMapping属性)编码在元数据中,另一种语言编译器知道bean应该是什么意思。 在F#库中使用您喜欢的非托管约束时,可以轻松看到:
namespace FSharpLibrary type FSharpType<'T when 'T : unmanaged>() = class end
希望我做对了。 并在C#项目中使用:
class Program { static void Main(string[] args) { var obj = new Example(); // fine } } class Foo { } class Example : FSharpLibrary.FSharpType { }
编译并执行得很好,实际上根本没有应用约束。 它不可能,运行时不支持它。
因此,在ILDasm中打开一个小样本,我们看到以下F#代码
open System.Collections type Class1<'T when 'T : unmanaged> = class end type Class2<'T> = class end type Class3<'T when 'T :> IEnumerable> = class end
成为以下IL
.class public auto ansi serializable beforefieldinit FSharpLibrary.Class1`1 extends [mscorlib]System.Object { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 ) } // end of class FSharpLibrary.Class1`1 .class public auto ansi serializable beforefieldinit FSharpLibrary.Class2`1 extends [mscorlib]System.Object { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 ) } // end of class FSharpLibrary.Class2`1 .class public auto ansi serializable beforefieldinit FSharpLibrary.Class3`1<([mscorlib]System.Collections.IEnumerable) T> extends [mscorlib]System.Object { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 ) } // end of class FSharpLibrary.Class3`1
值得注意的是, Class2
具有无约束的generics参数,并且完美匹配Class1
即使T
在Class1
被约束为非unmanaged
。 相比之下, Class3
与这个给定的模式不匹配,我们可以清楚地看到IL中的显式:> IEnumerable
约束。
另外,还有以下C#代码
public class Class2 { } public class Class3 where T : IEnumerable { }
变
.class public auto ansi beforefieldinit CSharpLibrary.Class2`1 extends [mscorlib]System.Object { } // end of class CSharpLibrary.Class2`1 .class public auto ansi beforefieldinit CSharpLibrary.Class3`1<([mscorlib]System.Collections.IEnumerable) T> extends [mscorlib]System.Object { } // end of class CSharpLibrary.Class3`1
除了F#生成的构造函数( .ctor
s)和Serializable
标志之外,它与F#生成的代码匹配。
没有其他对Class1
引用因此意味着编译器在IL级别不考虑unmanaged
约束,并且不会在编译输出中留下其存在的进一步引用。
CorHdr.h中的CorGenericParamAttr枚举列出了CIL级别的所有可能的约束标志,因此非托管约束完全由F#编译器强制执行。
typedef enum CorGenericParamAttr { gpVarianceMask = 0x0003, gpNonVariant = 0x0000, gpCovariant = 0x0001, gpContravariant = 0x0002, gpSpecialConstraintMask = 0x001C, gpNoSpecialConstraint = 0x0000, gpReferenceTypeConstraint = 0x0004, gpNotNullableValueTypeConstraint = 0x0008, gpDefaultConstructorConstraint = 0x0010 } CorGenericParamAttr;
- 如何在Windows窗体应用程序中触发自动注销?
- 是否在任务取消正确时处理CancellationTokenSource的代码?
- ITextShape可点击的多边形或路径
- backgroundWorker的目的是什么? (我可以获得一些示例代码来理解吗?)
- 从字符串加载.Net中的Jira公共证书(如何在.Net中将ASN.1编码的SubjectPublicKeyInfo转换为X509证书)
- 如何将Master Page的ScriptManager放入c#(.cs)文件后面的Child页面代码中
- 在具有母版页的页面上查找控件
- 根据自签名证书颁发机构validation服务器证书
- JsonObject模拟Facebook SDK