获取参数的调用变量名称

Sok*_*oko 4 .net c# ixmlserializable nameof

关于问题从调用方法获取参数名称查找传递给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<T>(string name, T says)
    {
        MessageBox.Show("The " + name + " says: " + says);
        // Shows i.e.: The dog says: WauWau
    }

    public void WhatDoesTheAnimalSay_WRONG1<T>(T says)
    {
        MessageBox.Show("The " + nameof(says) + " says: " + says);
        // Shows: The says says: WauWau
    }

    public void WhatDoesTheAnimalSay_WRONG2<T>(T says, [CallerMemberName] string name = null)
    {
        MessageBox.Show("The " + name + " says: " + says);
        // Shows: The MainFunction says: WauWau
    }
    public void WhatDoesTheAnimalSay_WANTED<T>(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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

所以,遗憾的是,我无法引入新的包装类,接口或更改动物名称或保存内容的位置.这意味着它必须使用类和纯字符串,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<BuildingSite> BuildingSites { get; set; }

        // The list of all fitters working for the company
        public List<Fitter> 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<T>(this XmlWriter writer, string elementName, IEnumerable<T> items)
        {
            var list = items is List<T> ? 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....
    }
}
Run Code Online (Sandbox Code Playgroud)

abt*_*bto 10

您可以使用带Expression<Func<object>>参数的方法:

public void WhatDoesTheAnimalSay_WANTED(Expression<Func<object>> expression)
{
    var body = (MemberExpression)expression.Body;
    var variableName = body.Member.Name;

    var func = expression.Compile();
    var variableValue = func();

    MessageBox.Show("The "+ variableName + " says: " + variableValue);
}
Run Code Online (Sandbox Code Playgroud)

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

称之为:

WhatDoesTheAnimalSay_WANTED(() => dog)
WhatDoesTheAnimalSay_WANTED(() => Cow)
WhatDoesTheAnimalSay_WANTED(() => Cat)
WhatDoesTheAnimalSay_WANTED(() => kiwi)
Run Code Online (Sandbox Code Playgroud)

常量不可能,因为编译器将在编译时给定的值替换常量占位符:

const string constValue = "Constant Value";

WhatDoesTheAnimalSay_WANTED(() => constValue)
Run Code Online (Sandbox Code Playgroud)

会变成

WhatDoesTheAnimalSay_WANTED(() => "Constant Value")
Run Code Online (Sandbox Code Playgroud)

制作expression.Body类型ConstantExpression,这会在运行时产生异常.

所以你必须要小心你提供的那种方法的表达方式.

补充说明

您可以从下面的注释中注意到,使用lambda表达式来收集变量名称似乎存在争议.

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

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

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

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

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

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


归档时间:

查看次数:

1826 次

最近记录:

9 年,3 月 前