【问题标题】:How can I efficiently switch on an expression result?如何有效地打开表达式结果?
【发布时间】:2015-06-22 01:10:42
【问题描述】:

我不确定我的措辞是否正确,但我想做的是这样。我使用有这样的代码:

Select Case name
  Case "ThisProperty": ...
  Case "ThatProperty": ...
  ...
End Select

name 是对象的属性,我想以类型安全的方式执行此操作,因此如果属性更改,我会将其作为编译时错误捕获。所以我想要这种伪代码

Select Case name
  Case PropertyName(Of MyType)(Function(x) x.ThisProperty): ...
  Case PropertyName(Of MyType)(Function(x) x.ThatProperty): ...
  ...
End Select

在哪里

Function PropertyName(Of T)(propertySpecifier As Expression(Of Func(Of T, Object))) As String
    Return {expression magic to get property name as a string}
End Function

我的PropertyName 函数工作正常,但你不能像这样编写Select Case,即使你可以,我担心它会非常低效,无论如何,在一个紧密的循环中,因为所有表达式操作。 (而且语法也很乏味。)

所以我想知道是否有一种合理的方式来编写代码来解决这两个(三个)问题。

【问题讨论】:

  • 您要处理多少个值?也许尝试一个枚举?
  • 这在 VB.NET 14 (VS 2015) 中会容易得多,因为他们正在添加 NameOf 运算符。
  • @JacobRoberts:然后我只需要在属性名称更改时手动更新 Enum,这只会将问题转移到其他地方。我正在尝试做的事情是让编译器在我更改某些内容时自动捕获它。
  • @StevenDoggart:哦,太酷了!我想我需要在尝试之前获得那个版本,但它看起来很有希望。

标签: .net vb.net generics lambda clr


【解决方案1】:

Steven Doggart 说的很对。这是一个例子。我已经在进行名称绑定表达式评估的情况下使用了 NameOf,它替换了我所有的文字字符串,从而对名称重构具有弹性。

(FWIW,VS2015 CTP6 -(撰写本文时的当前版本)-稳定,VB 和 C# 功能齐全)

Sub Main()
    Dim name = "x"

    Select Case name.ToUpperInvariant
        Case NameOf(Grover.x).ToUpperInvariant
            Console.WriteLine("Monkey")
        Case NameOf(Grover.y).ToUpperInvariant
            Console.WriteLine("Giraffe")
        Case NameOf(Grover.Cheese).ToUpperInvariant
            Console.WriteLine("Ferret")
    End Select

    Console.ReadKey()
End Sub

Public Class Grover
    Public x As Integer
    Public y As Integer
    Public Cheese As String
End Class

【讨论】:

  • 哦,太可爱了。我可以看到NameOf 将如何让事情变得更好。
【解决方案2】:

正如我在评论中所说,NameOf 运算符将是您在即将到来的 VB.NET 14 中的救命稻草,这将在 Visual Studio 2015 中出现。您也不必担心它的效率,因为它将在编译时评估,而不是在运行时使用反射。

与此同时,除了创建自己的代码生成预构建操作之外,您最好的选择可能是仅通过反射评估属性名称一次,然后缓存结果。例如,您可以创建一个动作字典,如下所示:

Dim actions As New Dictionary(Of String, Action)()
actions.Add(PropertyName(Of MyType)(Function(x) x.ThisProperty),
            Sub() Console.WriteLine("This"))
actions.Add(PropertyName(Of MyType)(Function(x) x.ThatProperty),
            Sub() Console.WriteLine("That"))

然后,您可以从字典中提取所需的操作并执行它,而不是 Select Case

actions(name).Invoke()

至少在构建字典时您只需要获取一次属性名称。构建完成后在字典中找到它们会非常快。不过,对于一个简短的列表,您可能只想使用 SortedList 而不是 Dictionary

【讨论】:

    【解决方案3】:

    这里有一个正是为了这个目的的例程。它在 C# 中。 但是VB。接受表达式的静态类的净等效应该是可能的。也许有一个 vb 兄弟知道如何转换它。

    如何处理引用 POCO 属性的字符串并在它们更改时出现编译时错误。 您使用一个小的 lamda 表达式来引用该属性,然后您得到的是 name 作为字符串返回。

     public static class MyExtUtilites {
     public static string PropNameAsExpr<TPoco, TProp>(Expression<Func<TPoco, TProp>> prop)      {
    
            var body = prop.Body as System.Linq.Expressions.MemberExpression;
            return body == null ? null : body.Member.Name;
        }
      }
    

    在需要将属性名称作为字符串的地方使用它。 在编译时,属性 propname 必须在 MyPoco 类中是已知的。

      var propNameAsString =  myExtUtilites.PropNameAsExpr((MyPoco p) => myPoco.propname)     ;
    

    来自进行 c# 到 vb 网络转换的随机网站。不确定它是否正常。

     Imports System
     Imports System.Linq
     Imports System.Linq.Expressions
    
     Public static Class MyExtUtilites
    Public static String PropNameAsExpr<TPoco, TProp>(Expression<Func<TPoco, TProp> prop)      
    {
    
        Dim body As var =  prop.Body as System.Linq.Expressions.MemberExpression 
        Return body  =  Nothing ? Nothing : body.Member.Name
    }
    End Class
    

    【讨论】:

    • 这就是我的PropertyName 函数所做的,它工作正常。我的问题是关于如何有效地使用它(即在紧密循环中调用例程可能会很慢)以及如何将它与 Select Case/switch 语法一起使用,这种语法会很尴尬甚至无法编译跨度>
    • 如果您担心速度,那么构建字典的前期阶段可能是一个不错的选择。抱歉,您认为您想要“未引用的代码”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多