Подтвердить что ты не робот

Поведение ограничения типа F # "неуправляемого" типа

F # поддерживает ограничение типа для "неуправляемого". Это не то же самое, что ограничение типа значения, такое как ограничения структуры. MSDN отмечает, что поведение неуправляемого ограничения:

Предоставляемый тип должен быть неуправляемым. Неуправляемые типы - это либо определенные примитивные типы (sbyte, byte, char, nativeint, unativeint, float32, float, int16, uint16, int32, uint32, int64, uint64 или decimal), типы перечисления, nativeptr < _ > или не общая структура, поля которой являются неуправляемыми типами.

Это очень удобный тип ограничения при вызове платформы, и не раз я хотел бы, чтобы у С# был способ сделать это. С# не имеет этого ограничения. С# не поддерживает все ограничения, которые могут быть указаны в CIL. Примером этого является перечисление. В С# вы не можете этого сделать:

public void Foo<T>(T bar) where T:enum

Однако компилятор С# соблюдает ограничение "enum", если оно встречается с ним в другой библиотеке. Jon Skeet может использовать это для создания своего проекта Unconstrained Melody.

Итак, мой вопрос заключается в том, что F # "неуправляемое" ограничение означает что-то, что может быть представлено в CIL, как ограничение перечисления и просто не показано на С#, или оно принудительно выполняется компилятором F #, как и некоторые другие ограничения F # поддерживает (например, явное ограничение Member)?

4b9b3361

Ответ 1

У меня есть обратная связь, остерегайтесь, что я не знаю, F # почти достаточно хорошо. Пожалуйста, отредактируйте, где я goof. Прежде всего, вначале среда исполнения не реализует ограничений, поддерживаемых F #. И поддерживает больше, чем поддерживает С#. Он имеет всего 4 типа ограничений:

  • должен быть ссылочным типом (ограничение класса в С#, а не struct в F #)
  • должен быть типом значения (ограничение структуры в С# и F #)
  • должен иметь конструктор по умолчанию (ограничение new() в С#, новое в F #)
  • ограничен типом.

Затем спецификация CLI устанавливает конкретные правила о том, как эти ограничения могут быть действительными для определенного типа параметра типа, разбитого на ValueType, Enum, Delegate, Array и любого другого произвольного типа.

Дизайнеры языка могут внедрять инновации на своем языке, если они соблюдают то, что может поддерживать среда исполнения. Они могут самостоятельно добавлять произвольные ограничения, у них есть компилятор для их принудительного применения. Или произвольно решите не поддерживать тот, который поддерживается во время выполнения, потому что он не соответствует их языковому дизайну.

Расширения F # работают нормально, пока общий тип используется только в коде F #. Таким образом, компилятор F # может обеспечить его выполнение. Но он не может быть проверен временем выполнения, и он не будет иметь никакого эффекта, если такой тип потребляется другим языком. Ограничение кодируется в метаданных с помощью специальных атрибутов F # (атрибут Core.CompilationMapping), компилятор другого языка знает beans то, что они должны означать. Легко видно, когда вы используете неуправляемое ограничение, которое вам нравится в библиотеке F #:

namespace FSharpLibrary

type FSharpType<'T when 'T : unmanaged>() =
    class end

Надеюсь, я понял это правильно. И используется в проекте С#:

class Program {
    static void Main(string[] args) {
        var obj = new Example();   // fine
    }
}
class Foo { }
class Example : FSharpLibrary.FSharpType<Foo> { }

Компилирует и выполняет просто отлично, ограничение вообще не применяется. Этого не может быть, среда выполнения не поддерживает его.

Ответ 2

Итак, открыв небольшой образец в 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 привязан к unmanaged в Class1. Напротив, Class3 не соответствует данному шаблону, и мы можем четко видеть явное ограничение :> IEnumerable в IL.

Кроме того, следующий код С#

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 и не оставлять лишних ссылок на его присутствие на скомпилированном выходе.

Ответ 3

CorgenericParamAttr Enumeration в CorHdr.h перечисляет все возможные флаги ограничений на уровне CIL, поэтому неуправляемое ограничение принудительно выполняется компилятором F #.

typedef enum CorGenericParamAttr {
    gpVarianceMask                     =   0x0003,
    gpNonVariant                       =   0x0000, 
    gpCovariant                        =   0x0001,
    gpContravariant                    =   0x0002,

    gpSpecialConstraintMask            =   0x001C,
    gpNoSpecialConstraint              =   0x0000,
    gpReferenceTypeConstraint          =   0x0004, 
    gpNotNullableValueTypeConstraint   =   0x0008,
    gpDefaultConstructorConstraint     =   0x0010
} CorGenericParamAttr;