2014-12-29 2 views
19

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

Предоставленный тип должен быть неуправляемым типом. Неуправляемые типы - это либо определенные примитивные типы (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# компилятор делает честь «перечисление» ограничение, если речь идет через него в другой библиотеке. Jon Skeet может использовать это для создания своего проекта Unconstrained Melody.

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

+8

Глядя на быстрый пример в декомпиляторе, нет никакого ИЛ, который указывал бы, что тип ограничен. Я предполагаю, что информация включена в файл emdbedded signature и поэтому применяется только компилятором F #. –

+0

@mikez Да, это тот вывод, который я достиг. – vcsjones

+0

звучит как ответ. – eis

ответ

6

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

  • должен быть ссылочный тип (ограничение класса в C#, а не структуры в F #)
  • должен быть тип значения (структура ограничение в C# и F #)
  • сусла имеют конструктор по умолчанию (ограничение new() в C#, новое в 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> { } 

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

+3

Обратите внимание, что, хотя это фантастический пример, вы действительно должны использовать 'class Foo', поскольку пустая' struct' соответствует определению 'неуправляемого 'ограничения (' struct', который содержит только неуправляемые типы) – David

+0

Вы правы, благодаря! –

+0

Глядя на IL в ответе Аравола, похоже, что 'CompilationMapping' фактически не используется для кодирования ограничения. – svick

5

Таким образом, открытие небольшого образца в 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 с) и Serializable флаги, совпадает с F # сгенерированный код ,

Не имеет других ссылок на Class1 Таким образом, на компиляторе нет, на уровне IL, с учетом ограничения unmanaged и не оставлять никаких дальнейших ссылок на его присутствие на скомпилированном выходе.

4

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; 
Смежные вопросы