获取参数的调用变量名称

关于问题从调用方法获取参数 名称和查找传递给C#中函数的变量名称,我仍然在寻找一种方法来定义WhatDoesTheAnimalSay_WANTED方法:我想知道使用的变量的名称作为参数:

 public class Farm { public readonly string Cow = "Muuuuhh"; public string Cat { get; set; } public void MainFunction() { var dog = "WauWau"; var kiwi = new Bird("QueeeekQueeek"); Cat = "Miiiaaauuuu"; // This one works but looks kinda ugly and is cumbersome when used WhatDoesTheAnimalSay(nameof(dog), dog); WhatDoesTheAnimalSay(nameof(Cow), Cow); WhatDoesTheAnimalSay(nameof(Cat), Cat); WhatDoesTheAnimalSay(nameof(kiwi), kiwi); WhatDoesTheAnimalSay_WRONG1(dog); WhatDoesTheAnimalSay_WRONG2(dog); WhatDoesTheAnimalSay_WANTED(dog); WhatDoesTheAnimalSay_WANTED(Cow); } public void WhatDoesTheAnimalSay(string name, T says) { MessageBox.Show("The " + name + " says: " + says); // Shows ie: The dog says: WauWau } public void WhatDoesTheAnimalSay_WRONG1(T says) { MessageBox.Show("The " + nameof(says) + " says: " + says); // Shows: The says says: WauWau } public void WhatDoesTheAnimalSay_WRONG2(T says, [CallerMemberName] string name = null) { MessageBox.Show("The " + name + " says: " + says); // Shows: The MainFunction says: WauWau } public void WhatDoesTheAnimalSay_WANTED(T says /*, ?????*/) { MessageBox.Show("The " /*+ ?????*/ + " says: " + says); // Shows: The dog says: WauWau } } // Just here to show it should work with a class as well. public class Bird { public string Says { get; } //readonly public Bird(string says) { Says = says; } public override string ToString() { return Says; } } 

在现实生活中,我需要在我的类中使用自定义reader.Read...reader.Read... writer.Write....方法实现IXmlSerializable接口。

所以,遗憾的是,我无法引入新的包装类,接口或更改动物名称或保存内容的位置。 这意味着它必须使用类和纯字符串,int,decimal,…变量,字段或属性 。 换句话说(不是以粗鲁的方式):不要改变动物的定义方式,只需更改WhatDoesTheAnimalSay_WANTED方法……

编辑:

有些人想知道这个例子的真实用例,我在这里告诉你如何在xml文件中存储和读取数据。 真正的数据库对象当然更大,所有子类(如Fitter )也使用相同的扩展方法实现IXmlSerializable接口。

  // This is the Database Class which stores all the data public class Database : IXmlSerializable { // The list of all building sites managed public List BuildingSites { get; set; } // The list of all fitters working for the company public List Fitters { get; set; } private readonly int DatabaseVersion = 1; // Write implementation of the IXmlSerializable inteface public void WriteXml(XmlWriter writer) { // Writing all Data into the xml-file writer.WriteElementInt(nameof(DatabaseVersion), DatabaseVersion); writer.WriteElementList(nameof(BuildingSites), BuildingSites); writer.WriteElementList(nameof(Fitters), Fitters); } public void ReadXml(XmlReader reader) { // Do the reading here } public XmlSchema GetSchema() { return null; } } public class XmlExtensions { // Writing a list into the xml-file public static void WriteElementList(this XmlWriter writer, string elementName, IEnumerable items) { var list = items is List ? items : items.ToList(); // The XML-Element should have the name of the variable in Database!!! writer.WriteStartElement(elementName); writer.WriteAttributeString("count", list.Count().ToString()); var serializer = new XmlSerializer(typeof(T)); list.ForEach(o => serializer.Serialize(writer, o, XmlHelper.XmlNamespaces)); writer.WriteEndElement(); } public static void WriteElementInt(this XmlWriter writer, string elementName, int toWrite) { // The XMLElement should have the name of the variable in Database!!! writer.WriteElementString(elementName, toWrite.ToString(CultureInfo.InvariantCulture)); } // More here.... } } 

您可以使用将Expression>作为参数的方法:

 public void WhatDoesTheAnimalSay_WANTED(Expression> expression) { var body = (MemberExpression)expression.Body; var variableName = body.Member.Name; var func = expression.Compile(); var variableValue = func(); MessageBox.Show("The "+ variableName + " says: " + variableValue); } 

使用此方法可以处理各种变量(静态成员,实例成员,参数,局部变量等),也可以使用属性。

称之为:

 WhatDoesTheAnimalSay_WANTED(() => dog) WhatDoesTheAnimalSay_WANTED(() => Cow) WhatDoesTheAnimalSay_WANTED(() => Cat) WhatDoesTheAnimalSay_WANTED(() => kiwi) 

常量是不可能的,因为编译器会在编译时给出它的值替换常量占位符:

 const string constValue = "Constant Value"; WhatDoesTheAnimalSay_WANTED(() => constValue) 

会变成

 WhatDoesTheAnimalSay_WANTED(() => "Constant Value") 

制作expression.Body 。类型为ConstantExpression ,它会在运行时产生exception。

因此,您必须小心您提供的方法作为该方法的表达。

补充说明

正如您在下面的注释中所注意到的,使用lambda表达式来收集变量名称似乎存在争议。

正如@CodeCaster在他的一篇评论中指出的那样,没有正式指定编译器需要为匿名包装类中的捕获成员采用相同的局部变量名称。

但是,我在Expression 的评论中发现了这一点:

将表达式视为数据结构的能力使API能够以可以自定义方式检查,转换和处理的格式接收用户代码。

对我来说,这表明树的表达式正是为了这样的目的而设计的。

虽然Microsoft可能出于某种原因改变了这种行为,但似乎没有合理的需要。 依赖于最不惊讶的原则,我认为可以安全地假设对于()=> dogBody属性为MemberExpression ()=> dogMemberExpression ,该body.Member.Name解析为dog

如果还需要Body其他类型,则必须更多地计算方法。 并且有可能在某些情况下无论如何都不会起作用。

我担心目前没有办法实现你想要的。 本地变量名称不可用于被调用函数。

有一些替代品,你列出了一些。 另一种方法是将变量的名称作为属性放在类中。 无论如何,这将是更好的设计,因为应用程序中的数据不应该依赖于变量名称。 如果有人重命名了所有变量,您的应用程序应该以相同的方式工作。

其他语言(如C / C ++系列)为此目的使用预处理器宏,但它们在C#中不可用。 您可以在C ++ / CLI中实现这一点,但只能在没有选项的情况下在编译单元和/或程序集之外导出这样的宏。

所以 ,对不起,即使使用C#6及其新function,这也是不可能的。 它不应该是。 想想你的设计,你可能会想出一个更好的设计。