0

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.

Kosmo零
  • 4,001
  • 9
  • 45
  • 88
  • To me it looks like your `obj` is a simple `string` and its *content* is what you show in the screenshot. So even when you would change the variable name, the content would still be the same. – Daniel Hilgarth Oct 16 '13 at 08:02
  • It is possible to pass by ref, but the problems is still how to get it's name. – Kosmo零 Oct 16 '13 at 08:09
  • Maybe you can if you pass a lambda expression returning the field (`SetStr(() => lbl_labs_header.Text)`) instead of the field itself. Your SetStr method will need to be changed to accept a lambda expression, though. – Medinoc Oct 16 '13 at 08:24
  • Could you please show more how to use it and what SetStr should do with that labmba expression? this sounds good enough if will work and if I will be able to extract **ScenarioSelector.lbl_labs_header.Text** from it. – Kosmo零 Oct 16 '13 at 08:29

2 Answers2

3

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.

Medinoc
  • 6,577
  • 20
  • 42
  • Umm thank you, however I need another output. Like that: **TestLambdaExpression.toto**, **TestLambdaExpression.tutu.tata** – Kosmo零 Oct 16 '13 at 10:04
  • This is great now. Thank you. Just single parameter and all cool. Oh... But how to set the value? :O I need to set something to Text field, or another field passed to it... even customs field or properties. I updated my main question with what I have done, but still don't know how to get object instance to set it's value. – Kosmo零 Oct 16 '13 at 12:23
  • 1
    Wait, which direction are we talking? Do you want to read the value from `lbl_labs_header.Text` and write it into something? Or the other way around? – Medinoc Oct 16 '13 at 13:48
  • I want just set value to there. No need to read. ((FieldInfo)memberBody.Member).SetValue(/*How to get the object instance?*/, "some_val"); – Kosmo零 Oct 16 '13 at 14:10
  • So that's the trick **Expression.Assign**. Thank you for all your efforts. – Kosmo零 Oct 19 '13 at 07:02
1

I created a Form and placed a button.

When you click the button you will change it's Text field to the actual full typed name of the Text field - eg. "Stackoverflow.Form1.button1.Text"

namespace StackOverflow
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        SubjectInfo.SetStr(button1,() => button1,()=> button1.Text);
    }
}

class SubjectInfo
{
    public static void SetStr<T,U>(object obj,Expression<Func<T>> objLambda,     Expression<Func<U>> fieldLambda)
    {
        // get the name of the field
        string fieldName = ((MemberExpression) fieldLambda.Body).Member.Name;

        // get the full name of the whole object and field as it is typed in the source code
        string full_name_in_source = ((MemberExpression)objLambda.Body).Member.DeclaringType + "." + ((MemberExpression)objLambda.Body).Member.Name + "." + fieldName;

        obj.GetType().GetProperty(fieldName).SetValue(obj,full_name_in_source);
    }
}

}

user2023749
  • 75
  • 1
  • 5
  • Thank you. Too sad it's kind of... not that user-friendly to call function with 3 parameters, instead of single. But, well, at least this works :) I will accept your answer if no others more simple will appear. – Kosmo零 Oct 16 '13 at 10:02