Поведение ограничения типа 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)?
Ответы
Ответ 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;