【问题标题】:Generating classes at runtime在运行时生成类
【发布时间】:2017-08-17 10:47:12
【问题描述】:

我目前正在处理一个包含数据的文件。

数据可以在不同的类中,并且这些类在同一个文件中定义。

我目前能够将类输出到 json,并且我能够将数据作为 json 转换为这些类

问题是我希望能够动态创建一个类来处理移动中的数据,而不是简单地将其打印为文件。

类示例:

Class id:4 - name Item

public Int id
public I18N nameId
public Int typeId
public I18N descriptionId
public Int iconId
public Int level
public Int realWeight
public Bool cursed
public Int useAnimationId
public Bool usable
public Bool targetable
public Bool exchangeable
public Double price
public Bool twoHanded
public Bool etheral
public Int itemSetId
public String criteria
public String criteriaTarget
public Bool hideEffects
public Bool enhanceable
public Bool nonUsableOnAnother
public Int appearanceId
public Bool secretRecipe
public Int recipeSlots
public vector<UInt> recipeIds
public vector<UInt> dropMonsterIds
public Bool bonusIsSecret
public vector<EffectInstance> possibleEffects
public vector<UInt> favoriteSubAreas
public Int favoriteSubAreasBonus
public Int craftXpRatio
public Bool needUseConfirm
public Bool isDestructible
public vector<vector<Double>> nuggetsBySubarea
public vector<UInt> containerIds
public vector<vector<Int>> resourcesBySubarea

类型是从带有枚举的 Uint 定义的:

  Public Enum GameDataTypeEnum
        Int = -1
        Bool = -2
        [String] = -3
        [Double] = -4
        I18N = -5
        UInt = -6
        Vector = -99
    End Enum

为了解决这个问题,我尝试将枚举类型转换为 vb.net 类型,例如:

Dim myType1 As Type = Type.GetType("System.Int32")

然后我尝试像这样使用 Activator.CreateInstance:

Dim Type1 As Type = Type.GetType("System.String")
Dim Type2 As Type = Type.GetType("System.Int32")
Dim dictType As Type = GetType(Dictionary(Of , )).MakeGenericType(Type1, Type2)
Dim dict = Activator.CreateInstance(dictType)
dict.add("test", 32)
Console.WriteLine(dict.Item("test"))

这很好用,但是回顾这个解决方案它是不可行的,因为类有不同的类型。无论如何,您会看到从文件中创建类并动态生成它们吗?

最坏的情况是我可以手动生成所有类,但是有很多类并且需要很多时间


Vincent 的解决方案可以很好地解决一个小问题。

当我读取文件时,一个字段可能包含另一个字段,这些字段称为向量,可以表示为:List Of (Contained Value)

现在我只需这样做:

For Each field As GameDataField In classDefinition.Value.Fields
    Console.WriteLine(field.fieldName & " --- " & testcast(getFieldTypeString(field)))
    Console.WriteLine(field.fieldName & " --- " & field.fieldType)
    ClassProperties.Add(field.fieldName, Type.GetType(testcast(getFieldTypeString(field))))
Next

我的testcast() 函数只是返回一个字符串,将枚举转换为正确的格式,例如IntSystem.Int32

对于我的 getFieldTypeString 我有:

If isPrimitiveFieldType(field) Then
    Return getPrimitiveFieldTypeString(field)
Else
    Return getCompositeFieldTypeString(field)
End If

原始给出:

Private Function getPrimitiveFieldTypeString(field As GameDataField) As String
    Return If(field.fieldType > 0, classDefinitions(CInt(field.fieldType)).Name, field.fieldType.ToString())
End Function

复合给出:

Private Function getCompositeFieldTypeString(field As GameDataField) As String
    Dim compositeFieldTypeBuilder As New StringBuilder()
    compositeFieldTypeBuilder.Append("System.Collections.Generic.List(Of ")
    If getFieldTypeString(field.innerField) = "UInt" Then
        compositeFieldTypeBuilder.Append("UInt32)")
    ElseIf getFieldTypeString(field.innerField) = "Int" Then
        compositeFieldTypeBuilder.Append("Integer)")
    Else
        compositeFieldTypeBuilder.Append(getFieldTypeString(field.innerField)).Append(")")
    End If
    Return compositeFieldTypeBuilder.ToString()
End Function

如何向 ClassProperties 添加一个属性来表示:

List of a List of a Type

【问题讨论】:

  • @VisualVincent 回想一下我不认为字典方法是个好主意,因为在类中有不同的类型。所以就像在示例类中一样,我无法放置所有数据。是否有可能有类似的方法,但使用类?
  • 这看起来很有希望:stackoverflow.com/a/26971289/3740093
  • 此外,如果您使用该代码,请在此答案中进行建议的更改:stackoverflow.com/a/27499618/3740093
  • 如果你想 set 属性,那么我的 GetProperty 方法将不起作用。一会儿我会写一个更完整的答案。 -- 另外,请记住,Dim obj(1) As Object 创建了一个由 两个 元素组成的数组,因为您声明了它的 上界索引我>。要使用一项声明它,请执行 Dim obj(0) ... 或我喜欢的方式:Dim obj(1 - 1) ...

标签: vb.net class casting


【解决方案1】:

正如 cmets 中已经提到的,this answer(连同this fix)将帮助您动态创建类。

要获取/设置这些类的属性,我们可以创建两个使用反射的扩展方法,以便按名称访问属性 getter/setter:

Imports System.Runtime.CompilerServices

Public Module Extensions

    ''' <summary>
    ''' Uses Reflection to get the value of the specified property.
    ''' </summary>
    ''' <param name="Instance">The object instance which's property to access.</param>
    ''' <param name="PropertyName">The name of the property which's value to get.</param>
    ''' <param name="Arguments">Arguments to pass along to the property method (if any).</param>
    ''' <remarks></remarks>
    <Extension()> _
    Public Function GetProperty(ByVal Instance As Object, ByVal PropertyName As String, Optional ByVal Arguments As Object() = Nothing) As Object
        Return Instance.GetType().GetProperty(PropertyName).GetValue(Instance, Arguments)
    End Function

    ''' <summary>
    ''' Uses Reflection to set the value of the specified property.
    ''' </summary>
    ''' <param name="Instance">The object instance which's property to access.</param>
    ''' <param name="PropertyName">The name of the property which's value to set.</param>
    ''' <param name="Value">The new value to give the property.</param>
    ''' <param name="Arguments">Arguments to pass along to the property method (if any).</param>
    ''' <remarks></remarks>
    <Extension()> _
    Public Sub SetProperty(ByVal Instance As Object, ByVal PropertyName As String, ByVal Value As Object, Optional ByVal Arguments As Object() = Nothing)
        Instance.GetType().GetProperty(PropertyName).SetValue(Instance, Value, Arguments)
    End Sub
End Module

注意:

由于您似乎无法在 System.Object 上使用扩展方法,因此您只需正常调用它们即可:

Extensions.SetProperty(MyObject, "MyProperty", MyValue)

但是,对于任何不是直接 System.Object 的东西,您可以像往常一样使用这些扩展方法:

Button1.SetProperty("Text", "Click me!")

现在你可以像这样使用它们了:

'Just a quick mockup of properties.
Dim ClassProperties As New Dictionary(Of String, Type) From {
    {"name", Type.GetType("System.String")},
    {"id", Type.GetType("System.Int32")},
    {"hasCar", Type.GetType("System.Boolean")}
}

'Create the class type.
Dim CustomClass As Type = CreateClass("Person", ClassProperties)

'Create an instance of the class.
Dim MyPerson As Object = Activator.CreateInstance(CustomClass)

'Modify its properties.
Extensions.SetProperty(MyPerson, "name", "John Doe")
Extensions.SetProperty(MyPerson, "id", 12345)
Extensions.SetProperty(MyPerson, "hasCar", True)

MessageBox.Show( _
        String.Join(Environment.NewLine, {Extensions.GetProperty(MyPerson, "name"), _
                                          Extensions.GetProperty(MyPerson, "id"), _
                                          Extensions.GetProperty(MyPerson, "hasCar")}), _
        "", MessageBoxButtons.OK, MessageBoxIcon.Information)

利润!

【讨论】:

  • 这真的很好用!非常感谢,但是当我在有时读取的文件中动态创建我的类属性 dic 时有一个小问题:List (Of List Of (UInt)) 这将翻译为:System.Collections.Generic.List(Of System.Collections.Generic.List(Of UInt) 还是:System.Collections.Generic.List(Of List Of(UInt)
  • @Mederic : List(Of List Of (UInt)) 语法无效。你从哪里得到那部分?你有样品吗?
  • 在我的文件读取中,我有一个子区域的整数列表列表。在文件中,我可以有我所谓的 Primitive 或 Composite 字段,因为某些字段的类型为 (-99),它在 ActionScript 中表示转换为 List (Of T) 的 Vector 问题是我将如何定义一个属性是一个类型列表的列表。如果你不明白我可以编辑我的问题以便于解释
  • 我编辑了问题的底部以尝试更好地解释
  • @Mederic :不用等待,应该是System.UInt32 而不是System.UInteger(其余的都是正确的)。我的错,对不起。 :#
猜你喜欢
  • 1970-01-01
  • 2011-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-08
  • 2023-03-03
相关资源
最近更新 更多