F#“​​非托管”类型约束的行为

F#支持“非托管”的类型约束。 这与“结构”约束等值类型约束不同。 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>(T bar) where T:enum

但是,如果C#编译器遇到另一个库中的“enum”约束,它将遵守这个约束。 Jon Skeet能够用它来创建他的Unconstrained Melody项目。

所以,我的问题是,F#的“非托管”约束可以用CIL来表示,就像一个枚举约束,并且不在C#中公开,或者像F#编译器一样纯粹由F#编译器强制执行,就像F#支持的其他约束一样显式成员约束)?


我有一些反馈意见,请注意我几乎不知道F#。 请编辑我的位置。 首先要了解基础知识,运行时并未实际实现F#支持的约束。 并且支持比C#支持的更多的功能。 它只有4种约束:

  • 必须是引用类型(C#中的类约束,而不是F#中的结构)
  • 必须是一个值类型(C#和F#中的结构约束)
  • 必须有一个默认的构造函数(C#中的new()约束,F#中的新约束)
  • 由类型限制。
  • 然后,CLI规范将设置关于这些约束如何在特定类型参数类型上有效的特定规则,并由ValueType,Enum,Delegate,Array和任何其他任意类型分解。

    语言设计者可以自由地用他们的语言进行创新,只要他们遵守运行时支持的内容即可。 他们可以自己添加任意约束,他们有一个编译器来执行它们。 或者任意选择不支持运行时支持的语言,因为它不适合他们的语言设计。

    只要泛型类型只用于F#代码,F#扩展就可以正常工作。 所以F#编译器可以执行它。 但是它不能被运行时验证,如果这种类型被另一种语言所占用,它将不会产生任何效果。 该约束使用F#特定属性(Core.CompilationMapping属性)编码在元数据中,另一种语言编译器知道它们应该表示的意思。 当您在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<Foo> { }
    

    编译并执行得很好,约束实际上并不适用。 它不能,运行时不支持它。


    因此,在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<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.Class1`1
    
    .class public auto ansi serializable beforefieldinit FSharpLibrary.Class2`1<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.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有一个无约束的泛型参数,并且完全匹配Class1 ,尽管T被限制在Class1 unmanaged 。 相比之下, Class3不符合这个给定的模式,我们可以清楚地看到IL中的显式:> IEnumerable约束。

    另外,下面的C#代码

    public class Class2<T>
    { }
    
    public class Class3<T>
        where T : IEnumerable
    { }
    

    .class public auto ansi beforefieldinit CSharpLibrary.Class2`1<T>
           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;
    
    链接地址: http://www.djcxy.com/p/80663.html

    上一篇: Behavior of F# "unmanaged" type constraint

    下一篇: Undocumented `when` keyword usage in FSharp.Core