通过Visual Studio和C#/VB.NET中的代码生成破解元编程

本文关键字:代码生成 破解 编程 NET VB Studio Visual 通过 | 更新日期: 2023-09-27 17:58:55

我正试图破解一种生成代码的机制,这样我就可以动态地做某些事情,同时保留强类型的好处。

这是我想做的事情的一个例子。我有一个这样的对象:

Class Fruits
    Public Property Apple As System.Int32
    Public Property Orange As System.Int32
    Public Property Kiwi As System.Int32
    ...quite a few others
    Public Property Banana As System.Int32
    Public Property Watermelon As System.Int32
    Public Property Durian As System.Int32
End Class

我有一些代码可以做到这一点:

Dim dict = myEntity.ToDictionary()
Dim myFruits = {"Apple", "Banana", "Cherry"} 
For Each fruit in myFruits
   Dim fruitValue = dict(fruit)
   doSomething(fruitValue)
Next

换句话说,我有一个强类型类,我想访问它的属性,但这些属性是由代码确定的字符串。

我想避免的问题是,我的Fruits类可能会更改——例如,Apple属性被删除。这可能是因为它是通过代码生成创建的,也可能是因为我正在重构所以循环中的代码被破坏了,但编译器没有办法警告我这一点


到目前为止,上面的代码相当于这个:

   fruitValue = item.Apple
   doSomething(fruitValue)
   fruitValue = item.Banana
   doSomething(fruitValue)
   fruitValue = item.Cherry
   doSomething(fruitValue)

如果我有这段代码并删除了Apple,我会得到一个编译错误,一切都会好起来。

但事实上,我的水果清单可能会有更多的项目和/或由一些复杂的设计时规则决定,所以把它们都写出来会非常乏味和烦人。


我想要的是一种编写代码的方法,该代码在设计时由Visual Studio预处理,以生成内联、类型安全的代码

我想象的是一个系统和语法,我可以指定一个模板,该模板的输入,以及某种AddIn,它可以扫描代码文件,并在部分类文件中生成所需的代码。然后,我可以编写简洁而动态的代码,但要将其扩展为显式属性访问,如果底层对象发生更改,则会导致编译器错误。

所以问题是:如何实现这一点

通过Visual Studio和C#/VB.NET中的代码生成破解元编程

您可以使用类似的东西来动态获取对象的所有属性:

var fruits = new Fruits();
foreach (var prop in fruits.GetType().GetProperties())
{
    // Do stuff here
}

这样,当你更新对象属性时,上面的代码仍然会遍历它们所有的

请参阅msdn了解更多

您可以使用反射。我会创建一个扩展方法来获取属性的值:

public static object GetPropvalue(this object InputObject, string PropertyName)
{
    Type type = InputObject.GetType();
    PropertyInfo p = type.GetProperty(PropertyName);
    if (p != null)
    {
        return p.GetValue(InputObject);
    }
    else
    {
        return null;
    }
}

然后你可以做

foreach (var f in myFruits)
{
    var fruitValue = myEntity.GetPropValue(f);
    if (fruitValue != null)
    {
        doSomething(fruitValue));
    }       
}

这个例子将在编译时完成您的场景的工作,而不需要元编程

Public Class Fruits
    Public Property Apple As Integer
    Public Property Orange As Integer
    Public Property Kiwi As Integer        
End Class

以及使用NameOf表达式和Fruits类的伪实例的代码。

Dim dict = myEntity.ToDictionary()
Dim dummy As New Fruits()
Dim myFruits As New List(Of String) From
{
    NameOf(dummy.Apple),
    NameOf(dummy.Orange),
    NameOf(dummy.Kiwi)
} 
For Each fruit in myFruits
    Dim fruitValue = dict(fruit)
    DoSomething(fruitValue)
Next  

这将适用于您描述的场景:如果Apple属性将被删除,编译器将引发一个错误。

但在我看来,这种解决方法看起来更像是代码气味(使用伪实例)。我相信,如果你能更多地讲述你的问题,你会得到更合适的解决方案。