Как я могу создать дискриминированный союз?

Если у меня что-то вроде этого:

type public ServiceActionType =
    | ServiceRequest
    | ServiceResponse
    | ServiceEventInvocation
    | ServiceEventSubscriptionRequest
    | ServiceMetadataRequest

как мне добавить один из этих элементов в стек, например:

IlGen.Emit(OpCodes.???, ???)

person sircodesalot    schedule 22.03.2013    source источник


Ответы (3)


В общем, размеченные объединения составляются как иерархии классов (что означает, что существует новый подкласс для каждого случая размеченного объединения). Однако в вашем случае ситуация проще, потому что ни один из случаев не несет никаких значений.

Если вы посмотрите на скомпилированный код, вы увидите, что сгенерированное представление .NET дает вам что-то вроде:

class ServiceActionType {
  public static ServiceActionType ServiceRequest { get; }
  public static ServiceActionType ServiceResponse { get; }
  public static ServiceActionType ServiceEventInvocation { get; }
  // etc. for all the other cases
}

Это означает, что если вы хотите построить одно из значений размеченного объединения, вам просто нужно вызвать метод получения свойства (статического), представляющего нужный вам случай.

Если бы у вас был случай с аргументами, скажем ServiceRequest of RequestInfo, тогда сгенерированный класс включал бы вместо этого NewServiceRequest метод, который принимал бы необходимые параметры:

public static ServiceActionType NewServiceRequest(RequestInfo info);

Тем не менее, я не совсем уверен, почему вы хотите это сделать, поэтому создание кода может быть не лучшим подходом. Вы также можете рассмотреть возможность использования цитат F #, которые могут быть скомпилированы в динамические методы, а создание цитаты, представляющей конструкцию случая DU, довольно просто с использованием Expr.NewUnionCase.

person Tomas Petricek    schedule 22.03.2013
comment
I'm not entirely sure why you want to do this, пытаясь обернуть пользовательский интерфейс в конкретный класс (для вызовов типов услуг). Разве нет лучшего способа сделать это? - person sircodesalot; 22.03.2013
comment
@sircodesalot Не уверен, что я полностью понимаю - вы пытаетесь автоматически создавать оболочки вокруг различаемых объединений F #, чтобы соответствовать некоторому типу класса / интерфейса, предоставляемому вызывающей стороной? (Звучит интересно...) - person Tomas Petricek; 22.03.2013
comment
Пользователь создает класс на стороне службы, который предоставляет доступ через некоторый интерфейс. При использовании указанной службы пользователь вызывает Conntect ‹T› () (где T - тип интерфейса), который создает конкретный прокси-сервер вокруг интерфейса для вызова службы. Часть отправки запроса требует определения того, какой вызов выполняется, поэтому мне нужно было знать, как добавить тип вызова в стек Emit. - person sircodesalot; 23.03.2013

Существующие ответы хороши, но обратите внимание, что пространство имен Microsoft.FSharp.Reflection имеет несколько помощников, которые упрощают получение элементов, которые вам небезразличны:

open Reflection
let cases = FSharpType.GetUnionCases(typeof<ServiceActionType>)
let serviceRequestMethod = FSharpValue.PreComputeUnionConstructorInfo(cases.[0])

Результат такой же, как у typeof<ServiceActionType>.GetMethod("get_ServiceRequest"), но вам не нужно беспокоиться о том, чтобы знать скомпилированную форму, которая может варьироваться в зависимости от того, есть ли поля у вариантов объединения.

person kvb    schedule 22.03.2013
comment
+1 Хорошая точка зрения. Это, безусловно, лучший способ получить объект информации о методе! - person Tomas Petricek; 22.03.2013

Возьмем, к примеру, ServiceActionType.ServiceRequest.

Я поместил следующее в LINQPad (режим программы F #), чтобы получить IL (но вы можете использовать ildasm.exe для той же цели):

type public ServiceActionType =
    | ServiceRequest
    | ServiceResponse
    | ServiceEventInvocation
    | ServiceEventSubscriptionRequest
    | ServiceMetadataRequest

ServiceRequest.Dump()

и это дало мне:

IL_0001:  call        Query_qdoovg+ServiceActionType.get_ServiceRequest
IL_0006:  call        LINQPad.FSharpExtensions.Extensions.Dump

Dump:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  box         03 00 00 1B 
IL_0007:  call        LINQPad.FSharpExtensions.Extensions.Dump
IL_000C:  ret         

ServiceActionType.get_ServiceMetadataRequest:
IL_0000:  ldsfld      Query_qdoovg+ServiceActionType._unique_ServiceMetadataRequest
IL_0005:  ret         

ServiceActionType.get_IsServiceMetadataRequest:
IL_0000:  ldarg.0     
IL_0001:  call        Query_qdoovg+ServiceActionType.get_Tag
IL_0006:  ldc.i4.4    
IL_0007:  ceq         
IL_0009:  ret         

ServiceActionType.get_ServiceEventSubscriptionRequest:
IL_0000:  ldsfld      Query_qdoovg+ServiceActionType._unique_ServiceEventSubscriptionRequest
IL_0005:  ret         

ServiceActionType.get_IsServiceEventSubscriptionRequest:
IL_0000:  ldarg.0     
IL_0001:  call        Query_qdoovg+ServiceActionType.get_Tag
IL_0006:  ldc.i4.3    
IL_0007:  ceq         
IL_0009:  ret         

ServiceActionType.get_ServiceEventInvocation:
IL_0000:  ldsfld      Query_qdoovg+ServiceActionType._unique_ServiceEventInvocation
IL_0005:  ret         

ServiceActionType.get_IsServiceEventInvocation:
IL_0000:  ldarg.0     
IL_0001:  call        Query_qdoovg+ServiceActionType.get_Tag
IL_0006:  ldc.i4.2    
IL_0007:  ceq         
IL_0009:  ret         

ServiceActionType.get_ServiceResponse:
IL_0000:  ldsfld      Query_qdoovg+ServiceActionType._unique_ServiceResponse
IL_0005:  ret         

ServiceActionType.get_IsServiceResponse:
IL_0000:  ldarg.0     
IL_0001:  call        Query_qdoovg+ServiceActionType.get_Tag
IL_0006:  ldc.i4.1    
IL_0007:  ceq         
IL_0009:  ret         

ServiceActionType.get_ServiceRequest:
IL_0000:  ldsfld      Query_qdoovg+ServiceActionType._unique_ServiceRequest
IL_0005:  ret         

ServiceActionType.get_IsServiceRequest:
IL_0000:  ldarg.0     
IL_0001:  call        Query_qdoovg+ServiceActionType.get_Tag
IL_0006:  ldc.i4.0    
IL_0007:  ceq         
IL_0009:  ret         

ServiceActionType.get_Tag:
IL_0000:  ldarg.0     
IL_0001:  ldfld       Query_qdoovg+ServiceActionType._tag
IL_0006:  ret         

ServiceActionType.__DebugDisplay:
IL_0000:  ldstr       "%+0.8A"
IL_0005:  newobj      Microsoft.FSharp.Core.PrintfFormat<Microsoft.FSharp.Core.FSharpFunc<Query_qdoovg+ServiceActionType,System.String>,Microsoft.FSharp.Core.Unit,System.String,System.String,System.String>..ctor
IL_000A:  call        Microsoft.FSharp.Core.ExtraTopLevelOperators.PrintFormatToString
IL_000F:  ldarg.0     
IL_0010:  callvirt    Microsoft.FSharp.Core.FSharpFunc<Query_qdoovg+ServiceActionType,System.String>.Invoke
IL_0015:  ret         

ServiceActionType.CompareTo:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  ldnull      
IL_0003:  cgt.un      
IL_0005:  brfalse.s   IL_0027
IL_0007:  ldarg.1     
IL_0008:  ldnull      
IL_0009:  cgt.un      
IL_000B:  brfalse.s   IL_0025
IL_000D:  ldarg.0     
IL_000E:  ldfld       Query_qdoovg+ServiceActionType._tag
IL_0013:  stloc.0     
IL_0014:  ldarg.1     
IL_0015:  ldfld       Query_qdoovg+ServiceActionType._tag
IL_001A:  stloc.1     
IL_001B:  ldloc.0     
IL_001C:  ldloc.1     
IL_001D:  bne.un.s    IL_0021
IL_001F:  ldc.i4.0    
IL_0020:  ret         
IL_0021:  ldloc.0     
IL_0022:  ldloc.1     
IL_0023:  sub         
IL_0024:  ret         
IL_0025:  ldc.i4.1    
IL_0026:  ret         
IL_0027:  ldarg.1     
IL_0028:  ldnull      
IL_0029:  cgt.un      
IL_002B:  brfalse.s   IL_002F
IL_002D:  ldc.i4.m1   
IL_002E:  ret         
IL_002F:  ldc.i4.0    
IL_0030:  ret         

ServiceActionType.CompareTo:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  ldarg.1     
IL_0003:  unbox.any   Query_qdoovg.ServiceActionType
IL_0008:  call        Query_qdoovg+ServiceActionType.CompareTo
IL_000D:  ret         

ServiceActionType.CompareTo:
IL_0000:  nop         
IL_0001:  ldarg.1     
IL_0002:  unbox.any   Query_qdoovg.ServiceActionType
IL_0007:  stloc.0     
IL_0008:  ldarg.0     
IL_0009:  ldnull      
IL_000A:  cgt.un      
IL_000C:  brfalse.s   IL_0033
IL_000E:  ldarg.1     
IL_000F:  unbox.any   Query_qdoovg.ServiceActionType
IL_0014:  ldnull      
IL_0015:  cgt.un      
IL_0017:  brfalse.s   IL_0031
IL_0019:  ldarg.0     
IL_001A:  ldfld       Query_qdoovg+ServiceActionType._tag
IL_001F:  stloc.1     
IL_0020:  ldloc.0     
IL_0021:  ldfld       Query_qdoovg+ServiceActionType._tag
IL_0026:  stloc.2     
IL_0027:  ldloc.1     
IL_0028:  ldloc.2     
IL_0029:  bne.un.s    IL_002D
IL_002B:  ldc.i4.0    
IL_002C:  ret         
IL_002D:  ldloc.1     
IL_002E:  ldloc.2     
IL_002F:  sub         
IL_0030:  ret         
IL_0031:  ldc.i4.1    
IL_0032:  ret         
IL_0033:  ldarg.1     
IL_0034:  unbox.any   Query_qdoovg.ServiceActionType
IL_0039:  ldnull      
IL_003A:  cgt.un      
IL_003C:  brfalse.s   IL_0040
IL_003E:  ldc.i4.m1   
IL_003F:  ret         
IL_0040:  ldc.i4.0    
IL_0041:  ret         

ServiceActionType.GetHashCode:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  ldnull      
IL_0003:  cgt.un      
IL_0005:  brfalse.s   IL_003C
IL_0007:  ldc.i4.0    
IL_0008:  stloc.0     
IL_0009:  ldarg.0     
IL_000A:  call        Query_qdoovg+ServiceActionType.get_Tag
IL_000F:  switch      (IL_0028, IL_002C, IL_0030, IL_0034, IL_0038)
IL_0028:  ldc.i4.0    
IL_0029:  stloc.0     
IL_002A:  ldloc.0     
IL_002B:  ret         
IL_002C:  ldc.i4.1    
IL_002D:  stloc.0     
IL_002E:  ldloc.0     
IL_002F:  ret         
IL_0030:  ldc.i4.2    
IL_0031:  stloc.0     
IL_0032:  ldloc.0     
IL_0033:  ret         
IL_0034:  ldc.i4.3    
IL_0035:  stloc.0     
IL_0036:  ldloc.0     
IL_0037:  ret         
IL_0038:  ldc.i4.4    
IL_0039:  stloc.0     
IL_003A:  ldloc.0     
IL_003B:  ret         
IL_003C:  ldc.i4.0    
IL_003D:  ret         

ServiceActionType.GetHashCode:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  call        Microsoft.FSharp.Core.LanguagePrimitives.get_GenericEqualityComparer
IL_0007:  call        Query_qdoovg+ServiceActionType.GetHashCode
IL_000C:  ret         

ServiceActionType.Equals:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  ldnull      
IL_0003:  cgt.un      
IL_0005:  brfalse.s   IL_0026
IL_0007:  ldarg.1     
IL_0008:  isinst      Query_qdoovg.ServiceActionType
IL_000D:  stloc.0     
IL_000E:  ldloc.0     
IL_000F:  brfalse.s   IL_0024
IL_0011:  ldarg.0     
IL_0012:  ldfld       Query_qdoovg+ServiceActionType._tag
IL_0017:  stloc.1     
IL_0018:  ldloc.0     
IL_0019:  ldfld       Query_qdoovg+ServiceActionType._tag
IL_001E:  stloc.2     
IL_001F:  ldloc.1     
IL_0020:  ldloc.2     
IL_0021:  ceq         
IL_0023:  ret         
IL_0024:  ldc.i4.0    
IL_0025:  ret         
IL_0026:  ldarg.1     
IL_0027:  ldnull      
IL_0028:  cgt.un      
IL_002A:  ldc.i4.0    
IL_002B:  ceq         
IL_002D:  ret         

ServiceActionType.Equals:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  ldnull      
IL_0003:  cgt.un      
IL_0005:  brfalse.s   IL_0022
IL_0007:  ldarg.1     
IL_0008:  ldnull      
IL_0009:  cgt.un      
IL_000B:  brfalse.s   IL_0020
IL_000D:  ldarg.0     
IL_000E:  ldfld       Query_qdoovg+ServiceActionType._tag
IL_0013:  stloc.0     
IL_0014:  ldarg.1     
IL_0015:  ldfld       Query_qdoovg+ServiceActionType._tag
IL_001A:  stloc.1     
IL_001B:  ldloc.0     
IL_001C:  ldloc.1     
IL_001D:  ceq         
IL_001F:  ret         
IL_0020:  ldc.i4.0    
IL_0021:  ret         
IL_0022:  ldarg.1     
IL_0023:  ldnull      
IL_0024:  cgt.un      
IL_0026:  ldc.i4.0    
IL_0027:  ceq         
IL_0029:  ret         

ServiceActionType.Equals:
IL_0000:  nop         
IL_0001:  ldarg.1     
IL_0002:  isinst      Query_qdoovg.ServiceActionType
IL_0007:  stloc.0     
IL_0008:  ldloc.0     
IL_0009:  brfalse.s   IL_0013
IL_000B:  ldarg.0     
IL_000C:  ldloc.0     
IL_000D:  call        Query_qdoovg+ServiceActionType.Equals
IL_0012:  ret         
IL_0013:  ldc.i4.0    
IL_0014:  ret         

ServiceActionType..ctor:
IL_0000:  ldarg.0     
IL_0001:  call        System.Object..ctor
IL_0006:  ldarg.0     
IL_0007:  ldarg.1     
IL_0008:  stfld       Query_qdoovg+ServiceActionType._tag
IL_000D:  ret         

Где IL_0001: call Query_qdoovg+ServiceActionType.get_ServiceRequest - это бит, который выполняет инструкцию call для получения значения ServiceActionType.ServiceRequest в стеке.

Что может быть испущено как: il.Emit(OpCodes.Call, typeof<ServiceActionType>.GetMethod("get_ServiceRequest"))

person Stephen Swensen    schedule 22.03.2013