如何在 C# 中使用 .NET 表达式 API 为枚举创建赋值表达式

How to create an assignment expression for the enum using the .NET Expression API in C#

使用 TypeBuilder (.NET 4.6) API 我在运行时创建了一个 Enum 对象; w.r.t。这里的动态代码生成是yield:

public enum WorkflowModelProperty
    {
        WorkflowModelDate   = 1,
        WorkflowModelTime   = 2,
        WorkflowModelHeader = 3,
        WorkflowModelTitle  = 4,
        WorkflowModelID     = 5,
        WorkflowModelOntologyRoot = 6,
    }

下面是用于生成枚举对象的代码:

// Create a dynamic assembly in the current application domain and allow it to be executed.

var dynamicAssemblyName = new AssemblyName(theAssemblyName);
var assemlbyBuilderFactory = await CurrentAppDomain.DefineDynamicAssembly, dynamicAssemblyName, BuilderAccess);

var moduleBuilderFactory = await assemlbyBuilderFactory.DefineDynamicModule(theLibraryModuleName, dynamicAssemblyName.Name + DLLPrefix);

TypeBuilder enumTypeBuilderFactory = moduleBuilderFactory.DefineType(theEnumDefinitionName, TypeAttributes.Public, typeof (Enum), null);

enumTypeBuilderFactory?.DefineField("value__", typeof(int), FieldAttributes.Private | FieldAttributes.SpecialName);

下面是将字段成员添加到 Enum TypeBuilder 对象的代码:

EnumHostInfoSetCache = EnumHostInfoSetCacheBuilder.ToImmutable();

// Okay we need to check to see if the internal states validate the DSL model passed into us.

enumHostInfoSet = EnumHostInfoSetCache.SingleOrDefault(
    enumHostEntry =>
        (enumHostEntry.Value.GetValueOrDefault().EnumDefintionName ==
         localTypeDefinitionDSL.TheEnumDefinitionName)).Value;

var enumBuilder = await enumHostInfoSet.GetValueOrDefault().EnumTypeBuilder;

EnumFieldBuilderMaterializer = EnumFieldBuilderConnectedObserver?.
                            SubscribeOn(Scheduler.CurrentThread).Subscribe(
                                delegate(Tuple<string, TEnumBaseType> enumFieldMetadata)
                                {
                                    try
                                    {
                                        var enumFieldName  = enumFieldMetadata.GetEnumFieldMemberName();
                                        var enumFieldValue = enumFieldMetadata.GetEnumFieldMemberValue();

                                        // Code Generate the Enum Field, thus creating a literal definition
                                        var enumFieldBuilder = enumBuilder.DefineField(enumFieldName, enumBuilder,
                                            FieldAttributes.Public | FieldAttributes.Literal | FieldAttributes.Static);

                                        // Set the Constant value on
                                        enumFieldBuilder.SetConstant(enumFieldValue);

                                        // Okay, at this point the Enum Metadata DSL Model is hooked-up to our source here and is not connected to
                                        // the Hot Observable source here in the Agent; as we push data into memory the Model should load itself.

                                        EnumFieldBuilderCacheBuilder?.Add(enumFieldName, enumFieldBuilder);
                                    }
                                    catch (Exception e)
                                    {
                                        throw new ProtoNexusAPIException(e.Message);
                                    }
                                },
                                delegate(Exception exception)
                                {
                                    CodeGenExceptionHandler(exception);
                                },

至此,我得到了生成的枚举类型的完整实例。现在我想在动态生成的枚举类型实例上创建一个赋值——代码如下:

delegate() // OnCompleted() Implementation!
                                {
                                    // Step 0: Prepare for generation of Dynamic Enum Assignment via Expression APIs

                                    var codeForEnumType    = enumBuilder.CreateType();
                                    var enumInstance       = Activator.CreateInstance(Enum.GetUnderlyingType(codeForEnumType));
                                    var enumInstanceX      = Activator.CreateInstance(codeForEnumType);
                                    var recordOfEnumFields = enumBuilder.DeclaredFields;

                                    // Okay let's set-up for dynamic materialization of Enum Assignment Expression code:

                                    try
                                    {
                                        // Experimental code for learning and teaching...

                                        var rightSideExp = Enum.ToObject(codeForEnumType, 2); // I know I can get to this ordinal value

                                        // Step 1: Enum has been created and is ready for use.
                                        var enumCode = Expression.Variable(enumBuilder, "enumCode");
                                        // Step 2: Okay we are ready to make the Assignment via the Expression.Assign API. We can set this up a number of ways
                                        var enumAssignmentSource = Expression.Constant(rightSideExp);  // This is the value we want to assign
                                        var enumRightHandSideA   = Expression.Constant(Enum.ToObject(codeForEnumType, 1), codeForEnumType);
                                        //var enumRightHandSideB   = Expression.Constant(3, codeForEnumType); // This DOES NOT WORK! - Argument does not Match!
                                        var enumRightHandSideC   = Expression.Constant(enumInstanceX, codeForEnumType);
                                        // Step 3: 
                                        var workFlowDisplayCommand = Expression.Assign(enumCode, enumRightHandSideA);  // DOES NOT WORK - Argument does not Match!
                                        workFlowDisplayCommand = Expression.Assign(enumCode, enumRightHandSideC);      // DOES NOT WORK
                                        workFlowDisplayCommand = Expression.Assign(enumCode, enumAssignmentSource);    // DOES NOT WORK
                                    }
                                    catch (Exception e)
                                    {
                                        Debug.WriteLine(e.Message);
                                    }

                                    localTypeDefinitionDSL.SetEnumType(codeForEnumType, ref recordOfEnumFields);
                                    localTypeDefinitionDSL.SetEnumTypeInstance(enumInstance);

                                    if (EnumFieldBuilderCacheBuilder != null)
                                    {
                                        try
                                        {
                                            // Setup the Rx HOT Subscription: 
                                            // Step 1: Load-up our Immutable Collection
                                            EnumFieldBuilderCache = EnumFieldBuilderCacheBuilder?.ToImmutable();
                                            // Step 2: Create our Observable Source
                                            EnumFieldBuilderCollectionSource =
                                                EnumFieldBuilderCache?.ToObservable(Scheduler.CurrentThread);
                                            // Step 3: Convert the Collection Source to a Hot Observable
                                            EnumFieldBuilderSubscriptionSource =
                                                EnumFieldBuilderCollectionSource?.Publish();
                                            EnumFieldBuilderSubscriptionSource.ObserveOn(Scheduler.CurrentThread);
                                            localTypeDefinitionDSL.LoadFieldBuilderSubscription(
                                                EnumFieldBuilderSubscriptionSource);
                                        }
                                        catch (Exception e)
                                        {
                                            throw new ApplicationException(e.Message);
                                        }
                                    }

                                    EnumFieldBuilderMaterializer?.Dispose();
                                });

这是运行时错误消息:类型 'WorkflowMessagingCommands' 的表达式不能用于赋值给类型 'WorkflowMessagingCommands'

我错过了什么?!

在代码的第一步中,您必须更改 Expression.Variable 方法的第一个参数。此方法的第一个参数应该是您的类型,并且您没有传递正确的类型来创建变量。

你必须这样使用:

var enumCode = Expression.Variable(codeForEnumType, "enumCode");