Поведение ограничения типа F# "неуправляемый"
F# поддерживает ограничение типа для "неуправляемых". Это не то же самое, что ограничение типа значения, например ограничения "struct". MSDN Примечания что поведение неуправляемого ограничения:
предоставленный тип должен быть неуправляемым типом. Неуправляемые типы являются либо некоторые примитивные типы (тип sbyte, байт, символ, nativeint, unativeint, float32, поплавок, типа INT16, типа uint16, int32 и тип uint32, типа int64, uint64 В или decimal), типы перечисления, nativeptr, или неродовая структура, поля которой являются неуправляемыми типами.
это очень удобный тип ограничения при вызове платформы, и несколько раз я хотел бы, чтобы C# имел способ сделать это. C# не имеет этого ограничения. C# не поддерживает все ограничения, что can указывается в CIL. Примером этого является перечисление. В C# вы не можете этого сделать:
public void Foo<T>(T bar) where T:enum
однако компилятор C# выполняет ограничение" enum", если он сталкивается с ним в другой библиотеке. Джон Скит может использовать это, чтобы создать его Непринужденная Мелодия.
Итак, мой вопрос, является F#'ы "неуправляемый" ограничение-то, что может быть представлено в КСС, как перечисление ограничений и просто не выставляли в C#, или это действие чисто компилятор F#, как некоторые другие ограничения F# поддерживает (как явным членом ограничение)?
3 ответов
у меня есть обратная связь, остерегайтесь, что я не знаю F# достаточно хорошо. Пожалуйста, отредактируйте, где я лох. Во-первых, среда выполнения фактически не реализует ограничения, поддерживаемые F#. И поддерживает больше, чем поддерживает C#. Он имеет только 4 типа ограничений:
- должен быть ссылочным типом (ограничение класса В C#, а не struct в F#)
- должно быть типом значения (ограничение структуры в C# и F#)
- должен иметь значение по умолчанию ограничение конструктора (new () В C#, new В F#)
- ограничено по типу.
и спецификация CLI затем устанавливает конкретные правила о том, как эти ограничения могут быть действительны для определенного типа параметра типа, разбитого на ValueType, Enum, Delegate, Array и любой другой произвольный тип.
языковые дизайнеры могут свободно внедрять инновации на своем языке, если они соблюдают то, что может поддерживать среда выполнения. Они могут добавлять произвольные ограничения сами по себе, у них есть компилятор, чтобы принудить их. Или произвольно выбрать не поддерживать тот, который поддерживает среда выполнения, потому что он не соответствует их языковому дизайну.
расширения F# работают нормально, пока общий тип используется только в коде F#. Таким образом, компилятор F# может применить его. Но он не может быть проверен средой выполнения и не будет иметь никакого эффекта, если такой тип используется другим языком. Ограничение кодируется в метаданных с определенными атрибутами F# (Сердечник.Compilationmapping attribute), другой компилятор языка знает бобы, что они должны означать. Легко видно, когда вы используете неуправляемое ограничение, которое вам нравится в библиотеке 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
ограничен unmanaged
на Class1
. Напротив,Class3
не соответствует этому образцу, и мы можем ясно видеть явные :> IEnumerable
ограничение в IL.
кроме того, следующий 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
ограничение и не оставляйте дальнейших ссылок на его присутствие в скомпилированном выводе.
на Перечисление CorGenericParamAttr В 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;