获取参数的调用变量名 [英] Getting the calling variable name of a parameter

查看:168
本文介绍了获取参数的调用变量名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在关于这个问题的获取的参数,从名字主叫方法寻找传递到C#中的功能变量名称我还在寻找一种方式来定义 WhatDoesTheAnimalSay_WANTED 办法:我想知道这是使用的变量的名称作为一个参数:

 公共类农场
{
公共只读字符串牛=Muuuuhh;

公共字符串猫{搞定;组; }

公共无效MainFunction()
{
无功狗=WauWau;
变种猕猴桃=新鸟(QueeeekQueeek);

猫=Miiiaaauuuu;

//这一件作品,但看起来有点丑,在使用时比较麻烦
WhatDoesTheAnimalSay(nameof(狗),狗);
WhatDoesTheAnimalSay(nameof(牛),奶牛);
WhatDoesTheAnimalSay(nameof(猫),猫);
WhatDoesTheAnimalSay(nameof(猕猴桃),猕猴桃);

WhatDoesTheAnimalSay_WRONG1(狗);
WhatDoesTheAnimalSay_WRONG2(狗);

WhatDoesTheAnimalSay_WANTED(狗);
WhatDoesTheAnimalSay_WANTED(牛);
}

公共无效WhatDoesTheAnimalSay< T>(字符串名称,T表示)
{
MessageBox.Show(以下简称+姓名+云:+说的);
//显示如:狗说:WauWau
}

公共无效WhatDoesTheAnimalSay_WRONG1< T>(T称)
{
MessageBox.Show( 中的+ nameof(说)+云:+说);
//显示:该说认为:WauWau
}

公共无效WhatDoesTheAnimalSay_WRONG2< T>(T说,[CallerMemberName]字符串名称= NULL)
{
MessageBox.Show(以下简称+姓名+云:+说);
//显示为:MainFunction说:WauWau
}
公共无效WhatDoesTheAnimalSay_WANTED< T>(T说/ *,* ????? /)
{
MessageBox.Show(以下简称/ * + * ????? / +云:+说);
//显示:狗说:WauWau
}
}

//这里只是表明它应该与类正常工作。
公共类鸟
{
公共字符串说{搞定; } //只读
公鸟(字符串表示){
=说说;
}
公共重写字符串的ToString()
{
回报说;
}
}

在现实生活中,我需要这一点的同时实施的IXmlSerializable 在我的类进行自定义界面 reader.Read ... writer.Write ... 方法。



所以,很遗憾,我的不能引进新的包装类,接口或改变那里的动物或它说什么的名称保存。这意味着它具有类和一起工作一个普通字符串,整数,小数,...变量,字段或属性为好。
换句话说(不是在一个粗鲁的方式):不要更改如何动物的定义,只是改变了 WhatDoesTheAnimalSay_WANTED 方法...



编辑:



由于一些你想知道在这个例子中一个真实的使用情况下,我M让您在这里的想法我如何存储和放大器;在XML文件中读取数据。真实的数据库对象当然是这样做大的所有子类(如钳工)贯彻的IXmlSerializable 界面,以及。用同样的方法扩展

  //这是数据库类存储所有数据
公共类数据库: IXmlSerializable的
{
//所有建筑站点列表管理
公开名单< BuildingSite> BuildingSites {搞定;组; }

//为公司
公开名单<工作的所有识时务的名单;钳工>钳工{搞定;组; }

私人只读INT DatabaseVersion = 1;

//写执行的IXmlSerializable inteface
公共无效中WriteXML(XmlWriter的作家)
{
//所有写入数据到XML的文件
的writer.WriteElementInt(nameof(DatabaseVersion),DatabaseVersion);
writer.WriteElementList(nameof(BuildingSites),BuildingSites);
writer.WriteElementList(nameof(钳工),钳工);
}
公共无效的ReadXml(XmlReader中读取)
{
//在这里做阅读
}
公众的XmlSchema的getSchema(){返回NULL; }

}

公共类XmlExtensions
{
//写一个清单到XML文件
公共静态无效WriteElementList< T> (此XmlWriter的作家,字符串的ElementName,IEnumerable的< T>项目)
{
无功名单=项目是列表< T> ?项目:items.ToList();

//的XML的元素应该有变量的名称在数据库!
writer.WriteStartElement(的ElementName);
writer.WriteAttributeString(计数,list.Count()的ToString());
无功序列化=新的XmlSerializer(typeof运算(T));
list.ForEach(O => serializer.Serialize(作家,O,XmlHelper.XmlNamespaces));
writer.WriteEndElement();
}
公共静态无效WriteElementInt(此XmlWriter的作家,字符串的ElementName,诠释toWrite)
{
//的XMLELEMENT应该在数据库变量的名称!
writer.WriteElementString(的ElementName,toWrite.ToString(CultureInfo.InvariantCulture));
}

//这里更多...
}
}


解决方案

您可以使用的方法是采取一种表达式来; Func键<对象>> 作为参数:

 公共无效WhatDoesTheAnimalSay_WANTED(表达式来; Func键<对象>>表达式)
{
VAR体=(MemberExpression)expression.Body;
VAR VARIABLENAME = body.Member.Name;

VAR FUNC = expression.Compile();
VAR variableValue = FUNC();

MessageBox.Show(以下简称+ VARIABLENAME +云:+ variableValue);
}



使用这种方法提供了处理各种变量的能力(静态成员, 。实例成员,参数,局部变量等),性能也都是可能的。



调用它:

  WhatDoesTheAnimalSay_WANTED(()=>狗)
WhatDoesTheAnimalSay_WANTED(()=>牛)
WhatDoesTheAnimalSay_WANTED(()=>猫)
WhatDoesTheAnimalSay_WANTED( ()=>猕猴桃)

常量AR不可能的,因为编译器将取代通过不断的占位符它的价值,在编译时给出:

 常量字符串constValue =恒值; 

WhatDoesTheAnimalSay_WANTED(()=> constValue)



会被变换为

  WhatDoesTheAnimalSay_WANTED(()=>中恒值)

制作 expression.Body 常量表达式,这将产生在运行时异常。



所以,你必须要小心你提供尽可能表达的方法。



补充说明



你可以从下面的评论发现,使用lambda表达式来收集变量名似乎争议。



由于@CodeCaster在指出,他的评论之一,没有正式指定需要编译器采取的匿名包裹类抓获成员的局部变量的名称相同。



不过,我发现这的表达式来; TDelegate>




治疗表达式为数据结构的能力使得API来接收一个格式的用户代码,可检查,转换和一个自定义的方式进行处理。




对于我来说,这是一个迹象,表明表达式树是精确设计目的,如这



虽然可能的是微软改变了这种行为出于某种原因,似乎没有成为这样的逻辑需要。依托最小惊讶的原则我会说这是安全的假设,从表达式()=>狗机身属性类型 MemberExpression ,即体.Member.Name 解析为的



如果有必要有其他类型的正文此外,该方法已被制定出多一点点。而且也有可能,这将不会在某些情况下,反正工作


In relation to the question Get the name of parameters from a calling method and Finding the Variable Name passed to a Function in C# I'm still looking for a way to define theWhatDoesTheAnimalSay_WANTED method: I want to know the name of the variable that's used as a parameter:

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;
    }
}

In real life I need this while implementing the IXmlSerializable interface in my classes with custom reader.Read... and writer.Write.... methods.

So, unfortunately, I cannot introduce a new wrapper class, interface or change where the name of the animal or what it says is saved. Meaning it has to work with classes and with a plain string, int, decimal, ... variables, fields or properties as well. In other words (not in a rude way): Don't change how are the animals defined, just change the WhatDoesTheAnimalSay_WANTED method...

EDIT:

As some of you wanted to know a real use case for this example I'm giving you here an idea how I store & read data in an xml-file. The real Database object is of course way bigger and all sub-classes (like Fitter) implement the IXmlSerializable interface as well using the same extensions methods.

    // 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....
    }
}

解决方案

You can use a method which is taking an Expression<Func<object>> as parameter:

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);
}

Using this approach gives the ability to process a variety of variables (static members, instance members, parameters, local variables etc.), also properties are possible.

Call it like:

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

Constants ar not possible, because the compiler will substitute the constant placeholder by it's value, given at compile time:

const string constValue = "Constant Value";

WhatDoesTheAnimalSay_WANTED(() => constValue)

would be transformed to

WhatDoesTheAnimalSay_WANTED(() => "Constant Value")

making the expression.Body of type ConstantExpression, which would yield an exception at runtime.

So you have to be careful what you provide as expression to that method.

Additional Remarks

As you can notice from the comments below, the use of lambda expressions to gather variable names seems controversial.

As @CodeCaster pointed out in one of his comments, there is no officially specified need for the compiler to take the same name of a local variable for the captured member in the anonymous wrapping class.

However, I found this in the remarks of Expression<TDelegate>:

The ability to treat expressions as data structures enables APIs to receive user code in a format that can be inspected, transformed, and processed in a custom manner.

For me this is a sign that the expression trees are exactly designed for purposes like that.

Although it is possible that Microsoft changes this behavior for some reason, there does not seem to be a logical need for doing so. Relying on the principle of least astonishment I'd say it is safe to assume that for an expression from ()=> dog whose Body property is of type MemberExpression, that body.Member.Name resolves to dog.

If it is necessary to have other types for Body also, the method has to be worked out a little bit more. And also it is possible that this will not work in certain circumstances anyways.

这篇关于获取参数的调用变量名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆