【问题标题】: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 或其他什么?

标签: c# .net reflection


【解决方案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.");
        }
    }
}

【讨论】:

    猜你喜欢
    • 2012-01-08
    • 2018-11-16
    • 2020-03-17
    • 2010-09-14
    • 1970-01-01
    • 2014-06-27
    • 1970-01-01
    • 2011-02-15
    • 1970-01-01
    相关资源
    最近更新 更多