Search code examples
c#objectfieldinfofield-names

Is there a way to get field name including class containing it and another class containing this class if field passed into function as object?


I doing next thing... I pass into some function the field of some controls, like:

class ScenarioSelector
{
    public ScenarioSelector()
    {
        SubjectInfo.SetStr(lbl_labs_header.Text);
    }
}

class SubjectInfo
{
    public static void SetStr(object obj)
    {
        string name = obj.GetType().Name;
        string full_name = obj.GetType().FullName;
        Type t = obj.GetType();

        //FieldInfo fi = FieldInfo.GetFieldFromHandle(/*I don't have any handle here*/);

        //Somehow understand that passed object is... "ScenarioSelector.lbl_labs_header.Text" and set it's value depending from it.
    }
}

Function SetStr should understand the field name of passed object and set it's value depending of it's name. I tried something, but can't get what I need, I only getting System.String as name. enter image description here

Whole idea behind that is interface translation. I want to store in file something like: [ScenarioSelector.lbl_labs_header.Text][TEXT_ON_THIS_LABEL] And easy set it by calling function SetStr

Maybe you have some ideas of how to make it another way or fix my way?

P.S. Based on Medinoc example I did this:

static void Test3<T>(Expression<Func<T>> exp)
{
    Expression body = exp.Body;
    List<string> memberNames = new List<string>();
    MemberInfo previousMember = null;

    while(body.NodeType == ExpressionType.MemberAccess)
    {
        MemberExpression memberBody = (MemberExpression)body;
        string memberName = memberBody.Member.Name;

        if (previousMember == null) //this is first one
        {
            switch (memberBody.Member.MemberType)
            {
                case MemberTypes.Field:
                    ((FieldInfo)memberBody.Member).SetValue(/*How to get the object instance?*/, "some_val");
                    break;

                case MemberTypes.Property:
                    break;

                default:
                    break;
            }
        }

        if (memberBody.Expression.NodeType == ExpressionType.Constant && previousMember != null) //If it's the 'last' member, replace with type
            memberName = previousMember.DeclaringType.Name;

        memberNames.Add(memberName);
        previousMember = memberBody.Member;
        body = memberBody.Expression;
    }

    memberNames.Reverse();
    Console.WriteLine("Member: {0}", string.Join(".", memberNames));
}

But still don't know how to get instance of object to set needed value.


Solution

  • Without prior experience of lambda expressions, I managed to construct an example:

    using System;
    using System.Collections.Generic;
    using System.Linq.Expressions;
    using System.Reflection;
    
    namespace TestsA
    {
        class TestLambdaExpression
        {
            class Inner { public int tata; }
    
            string toto;
            Inner tutu;
    
            static void Test3<T>(Expression<Func<T>> exp)
            {
                Expression body = exp.Body;
                List<string> memberNames = new List<string>();
                while(body.NodeType == ExpressionType.MemberAccess)
                {
                    MemberExpression memberBody = (MemberExpression)body;
                    memberNames.Add(memberBody.Member.Name);
                    body = memberBody.Expression;
                }
                memberNames.Reverse();
                Console.WriteLine("Member: {0}", string.Join(".", memberNames));
    
            }
    
            public static void Test()
            {
                TestLambdaExpression obj = new TestLambdaExpression();
                obj.toto = "Hello world!";
                obj.tutu = new Inner();
                obj.tutu.tata = 42;
    
                Test3(() => obj.toto);
                Test3(() => obj.tutu.tata);
            }
        }
    }
    

    Calling TestLambdaExpression.Test should output:

    Member: obj.toto
    Member: obj.tutu.tata
    

    Edit:

    For your special output, a test can be added:

            static void Test3<T>(Expression<Func<T>> exp)
            {
                Expression body = exp.Body;
                List<string> memberNames = new List<string>();
                MemberInfo previousMember = null;
                while(body.NodeType == ExpressionType.MemberAccess)
                {
                    MemberExpression memberBody = (MemberExpression)body;
                    string memberName = memberBody.Member.Name;
                    //If it's the 'last' member, replace with type
                    if(memberBody.Expression.NodeType == ExpressionType.Constant && previousMember != null)
                        memberName = previousMember.DeclaringType.name;
                    memberNames.Add(memberName);
                    previousMember = memberBody.Member;
                    body = memberBody.Expression;
                }
                memberNames.Reverse();
                Console.WriteLine("Member: {0}", string.Join(".", memberNames));
    
            }
    

    Edit2: For modifying the variable, I managed to make this:

    private static void TestAssign<T>(Expression<Func<T>> exp, T toAssign)
    {
        Expression assExp = Expression.Assign(exp.Body, Expression.Constant(toAssign));
        Expression<Func<T>> newExp = exp.Update(assExp, null);
        newExp.Compile().Invoke();
    }
    

    It's probably not the fastest nor the most efficient way to do it, but it should be the most versatile.