从属性类型中获取属性名称

Posted

技术标签:

【中文标题】从属性类型中获取属性名称【英文标题】:Getting property name from within property type 【发布时间】:2021-12-04 16:03:12 【问题描述】:

我不知道如何更好地制定标题。我看到不少帖子标题相似,但讨论的内容完全不同。

所以我们开始吧。实际的实地情况很复杂,但我会尝试发布一个绝对简约的例子来描述它。

假设我们有一个名为Animal的类:

class Animal

  public void Run()
  
    try
    
      //try running
    
    catch(Exception e)
    
      MessageBox.Show(this.SomeCleverWayOfGettingPropertyName() + " failed to run");
    
  

现在我在另一个类中定义Animal类型的几个属性:

class Zoo

  public Animal Zebra get; set;
  public Animal Lion get; set;
  public Animal Rhino get; set;

  public void RunAll()
  
    Zebra.Run();
    Lion.Run();
    Rhino.Run();
  

我应该写什么来代替 SomeCleverWayOfGettingPropertyName() 让它显示动物的名称(即声明的属性的名称),例如“斑马无法运行”。

正如我所说,实际情况更复杂,所以请避免回答“为什么不重新设计整个代码库,而是尝试 X”之类的答案。我希望在System.Reflection 中找到一些东西来找出调用成员的名字,但我还没有找到类似的东西。

【问题讨论】:

@CodeCaster:嗯……那将是一个障碍。问题是Animal 的实例太多了,所以我试图创建一个集中位置来捕获Run 中的异常。如果我们知道它总是会在属性上而不是局部变量上被调用,它会有所帮助吗?通过强制将其转换为PropertyInfo 或其他什么? 【参考方案1】:

理想情况下,你会重新考虑你的问题,并可能赶在运行之外

根据您的具体需求,表达式可能会起作用。但它确实是一个糟糕的解决方案,如果您竭尽全力,您不妨在外面赶上,或者只是传递成员名称在。

给定

public class Animal

   public void Run()
   
      Console.WriteLine("Running");
   


public static class MemberInfoGetting

   public static void Run<T>(this Expression<Func<T>> memberExpression) where T : Animal
   
      var expressionBody = (MemberExpression)memberExpression.Body;
      try
      

         var animal = Expression.Lambda<Func<Animal>>(expressionBody).Compile()();
         animal.Run();
         throw new Exception("bob");
      
      catch
      
         Console.WriteLine($"expressionBody.Member.Name : failed to run");
      
   

用法

public static Animal Rhino  get; set;  = new Animal();

public static void Main()

   MemberInfoGetting.Run(() => Rhino);

输出

Running
Rhino : failed to run

【讨论】:

是的。它要求我编辑调用 Run 的所有位置,这是我想避免的。但是,谢谢,这是一些聪明的想法。 :)【参考方案2】:

用这种方法基本上是不可能的。当你拨打Zebra.Run()时会发生什么:

运行时调用自动生成的 get_Zebra() 方法,将 Zebra 的 Animal 实例指针放入堆栈。 运行时调用Animal.Run() 实例方法。

那时,关于该实例来自何处的所有变量/属性信息都几乎消失了。

现在Animal.Run() 不知道它是在来自属性的实例上调用的,并且不能保证它会被调用。它也可以是本地、方法参数或new()ed 实例,来自工厂或集合元素。您必须自己传递此信息。

或者,如果它是用于错误处理,它可能比您想象的更容易,而无需解决编译器魔法或昂贵的表达式重构:

在您的异常处理程序中,记录标识 Animal 实例的相关属性。结合堆栈跟踪,这应该可以为您提供足够的信息。

【讨论】:

谢谢。这是一些有见地的信息。我在帖子中没有提到(尽量减少)是我的属性类型为RelayCommand(MVVM Light)。由于 C# 在内部将属性转换为 get_*set_* 方法,因此只需在我的 Aniaml 的 Run 方法上调用 .Method.Name 即可返回 get_Zebra(正如您所提到的),这足以满足我的目的。非常感谢您的意见。【参考方案3】:

你可以试试这个:

class Animal

  public void Run([CallerMemberName] string caller = null)
  
    try
    
      //try running
    
    catch(Exception e)
    
      MessageBox.Show(caller + " failed to run");
    
  

【讨论】:

omg...如果它有效,那就太好了。让我试试。 不,不会,我自己试过了:D 大声笑...是的。它返回调用函数的名称 你愿意改成这个吗 => Zebra.Run(nameof(Zebra)); 没有。请参阅我在问题下的评论。但谢谢,它确实让我朝着正确的方向前进。【参考方案4】:

唯一合理的方法是更改​​RunAll(),使其监控每个调用,到现在修改的运行

class Animal

    static readonly Random rng = new Random();
    public bool Run()
    
        if (rng.NextDouble() < 0.5)
        
            return false;
        
        return true;
    


class Zoo

    ...
    public void RunAll()
    
        try
        

            if (!Zebra.Run())
            
                throw new Exception(nameof(Zebra));
            
            if (!Lion.Run())
            
                throw new Exception(nameof(Lion));
            
            if (!Rhino.Run())
            
                throw new Exception(nameof(Rhino));
            
        
        catch (Exception ex)
        

            Debug.WriteLine($"ex.Message failed to run.");
        
    

【讨论】:

以上是关于从属性类型中获取属性名称的主要内容,如果未能解决你的问题,请参考以下文章

从recordset.field.type 属性中获取ADO 数据类型的名称?

Java+反射1+获取属性/成员变量 的名称及类型

使用 lambda 表达式获取属性名称和类型

如何使用 lambda 表达式和匿名类型获取类型的属性名称?

c# 获取某个对象的[公有属性]的名称,类型,值

如何从 pyspark 数据框的模式属性(来自镶木地板文件)中获取特定字段名称的数据类型?