it-swarm-vi.tech

Tìm tên biến được truyền cho hàm

Hãy để tôi sử dụng ví dụ sau để giải thích câu hỏi của tôi:

public string ExampleFunction(string Variable) {
    return something;
}

string WhatIsMyName = "Hello World"';
string Hello = ExampleFunction(WhatIsMyName);

Khi tôi chuyển biến "WhatIsMyName" cho hàm ví dụ, tôi muốn có thể nhận được một chuỗi tên biến ban đầu. Có lẽ một cái gì đó như:

Variable.OriginalName.ToString()

Có cách nào để làm điều này?

56
GateKiller

Những gì bạn muốn không thể trực tiếp nhưng bạn có thể sử dụng Biểu thức trong C # 3.0:

public void ExampleFunction(Expression<Func<string, string>> f) {
    Console.WriteLine((f.Body as MemberExpression).Member.Name);
}

ExampleFunction(x => WhatIsMyName);

Lưu ý rằng điều này phụ thuộc vào hành vi không xác định và trong khi nó hoạt động trong trình biên dịch C # và VB của trình biên dịch Mono, trong trình biên dịch C # của Mono, không có gì đảm bảo rằng điều này sẽ không ngừng hoạt động trong các phiên bản trong tương lai.

57
Konrad Rudolph

Điều này không chính xác có thể, theo cách bạn muốn. C # 6.0 họ giới thiệu Toán tử tên sẽ giúp cải thiện và đơn giản hóa mã. Tên của toán tử giải quyết tên của biến được truyền vào nó.

Cách sử dụng cho trường hợp của bạn sẽ như thế này:

public string ExampleFunction(string variableName) {
      //Construct your log statement using c# 6.0 string interpolation
       return $"Error occurred in {variableName}";
}

string WhatIsMyName = "Hello World"';
string Hello = ExampleFunction(nameof(WhatIsMyName));

Một lợi ích chính là nó được thực hiện tại thời điểm biên dịch,

Biểu thức nameof là một hằng số. Trong mọi trường hợp, nameof (...) được đánh giá tại thời gian biên dịch để tạo ra một chuỗi. Đối số của nó không được đánh giá trong thời gian chạy và được coi là mã không thể truy cập (tuy nhiên nó không phát ra cảnh báo "mã không thể truy cập").

Thêm thông tin có thể được tìm thấy tại đây

Phiên bản cũ hơn của C 3.0 trở lên
[.__.] Để xây dựng trên câu trả lời của Nawfals

GetParameterName2(new { variable });

//Hack to assure compiler warning is generated specifying this method calling conventions
[Obsolete("Note you must use a single parametered AnonymousType When Calling this method")]
public static string GetParameterName<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    return typeof(T).GetProperties()[0].Name;
}
31
johnny 5
static void Main(string[] args)
{
  Console.WriteLine("Name is '{0}'", GetName(new {args}));
  Console.ReadLine();
}

static string GetName<T>(T item) where T : class
{
  var properties = typeof(T).GetProperties();
  Enforce.That(properties.Length == 1);
  return properties[0].Name;
}

Thêm chi tiết trong bài đăng trên blog này .

18
Rinat Abdullin

Ba cách:

1) Một cái gì đó không có sự phản ánh ở tất cả:

GetParameterName1(new { variable });

public static string GetParameterName1<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    return item.ToString().TrimStart('{').TrimEnd('}').Split('=')[0].Trim();
}

2) Sử dụng sự phản chiếu, nhưng cách này nhanh hơn hai cách khác.

GetParameterName2(new { variable });

public static string GetParameterName2<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    return typeof(T).GetProperties()[0].Name;
}

3) Chậm nhất trong tất cả, không sử dụng.

GetParameterName3(() => variable);

public static string GetParameterName3<T>(Expression<Func<T>> expr)
{
    if (expr == null)
        return string.Empty;

    return ((MemberExpression)expr.Body).Member.Name;
}

Để có được tên và giá trị tham số kết hợp, bạn có thể mở rộng các phương thức này. Tất nhiên thật dễ dàng để có được giá trị nếu bạn chuyển tham số riêng như một đối số khác, nhưng điều đó không phù hợp. Thay thế:

1)

public static string GetParameterInfo1<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    var param = item.ToString().TrimStart('{').TrimEnd('}').Split('=');
    return "Parameter: '" + param[0].Trim() +
           "' = " + param[1].Trim();
}

2)

public static string GetParameterInfo2<T>(T item) where T : class
{
    if (item == null)
        return string.Empty;

    var param = typeof(T).GetProperties()[0];
    return "Parameter: '" + param.Name +
           "' = " + param.GetValue(item, null);
}

3)

public static string GetParameterInfo3<T>(Expression<Func<T>> expr)
{
    if (expr == null)
        return string.Empty;

    var param = (MemberExpression)expr.Body;
    return "Parameter: '" + param.Member.Name +
           "' = " + ((FieldInfo)param.Member).GetValue(((ConstantExpression)param.Expression).Value);
}

1 và 2 có tốc độ tương đương bây giờ, 3 lại chậm chạp.

13
nawfal

Không, nhưng bất cứ khi nào bạn thấy mình làm những việc cực kỳ phức tạp như thế này, bạn có thể muốn nghĩ lại giải pháp của mình. Hãy nhớ rằng mã phải dễ đọc hơn viết.

4
Nate Kohari

Vâng! Có thể. Tôi đã tìm kiếm một giải pháp cho vấn đề này trong một thời gian dài và cuối cùng đã tìm ra một bản hack giải quyết nó (nó hơi khó chịu). Tôi không khuyên bạn nên sử dụng điều này như một phần của chương trình của bạn và tôi chỉ nghĩ rằng nó hoạt động trong chế độ gỡ lỗi. Đối với tôi điều này không quan trọng vì tôi chỉ sử dụng nó như một công cụ sửa lỗi trong lớp giao diện điều khiển của mình để tôi có thể làm:

int testVar = 1;
bool testBoolVar = True;
myConsole.Writeline(testVar);
myConsole.Writeline(testBoolVar);

đầu ra cho bàn điều khiển sẽ là:

testVar: 1
testBoolVar: True

Đây là chức năng tôi sử dụng để làm điều đó (không bao gồm mã gói cho lớp giao diện điều khiển của tôi.

    public Dictionary<string, string> nameOfAlreadyAcessed = new Dictionary<string, string>();
    public string nameOf(object obj, int level = 1)
    {
        StackFrame stackFrame = new StackTrace(true).GetFrame(level);
        string fileName = stackFrame.GetFileName();
        int lineNumber = stackFrame.GetFileLineNumber();
        string uniqueId = fileName + lineNumber;
        if (nameOfAlreadyAcessed.ContainsKey(uniqueId))
            return nameOfAlreadyAcessed[uniqueId];
        else
        {
            System.IO.StreamReader file = new System.IO.StreamReader(fileName);
            for (int i = 0; i < lineNumber - 1; i++)
                file.ReadLine();
            string varName = file.ReadLine().Split(new char[] { '(', ')' })[1];
            nameOfAlreadyAcessed.Add(uniqueId, varName);
            return varName;
        }
    }
4
blooop

System.En Môi.StackTrace sẽ cung cấp cho bạn một chuỗi bao gồm ngăn xếp cuộc gọi hiện tại. Bạn có thể phân tích cú pháp đó để lấy thông tin, bao gồm tên biến cho mỗi cuộc gọi.

2
kevin42

Hãy thử lớp Tiện ích này,

public static class Utility
{
    public static Tuple<string, TSource> GetNameAndValue<TSource>(Expression<Func<TSource>> sourceExpression)
    {
        Tuple<String, TSource> result = null;
        Type type = typeof (TSource);
        Func<MemberExpression, Tuple<String, TSource>> process = delegate(MemberExpression memberExpression)
                                                                    {
                                                                        ConstantExpression constantExpression = (ConstantExpression)memberExpression.Expression;
                                                                        var name = memberExpression.Member.Name;
                                                                        var value = ((FieldInfo)memberExpression.Member).GetValue(constantExpression.Value);
                                                                        return new Tuple<string, TSource>(name, (TSource) value);
                                                                    };

        Expression exception = sourceExpression.Body;
        if (exception is MemberExpression)
        {
            result = process((MemberExpression)sourceExpression.Body);
        }
        else if (exception is UnaryExpression)
        {
            UnaryExpression unaryExpression = (UnaryExpression)sourceExpression.Body;
            result = process((MemberExpression)unaryExpression.Operand);
        }
        else
        {
            throw new Exception("Expression type unknown.");
        }

        return result;
    }


}

Và người dùng thích

    /*ToDo : Test Result*/
    static void Main(string[] args)
    {
        /*Test : primivit types*/
        long maxNumber = 123123;
        Tuple<string, long> longVariable = Utility.GetNameAndValue(() => maxNumber);
        string longVariableName = longVariable.Item1;
        long longVariableValue = longVariable.Item2;

        /*Test : user define types*/
        Person aPerson = new Person() { Id = "123", Name = "Roy" };
        Tuple<string, Person> personVariable = Utility.GetNameAndValue(() => aPerson);
        string personVariableName = personVariable.Item1;
        Person personVariableValue = personVariable.Item2;

        /*Test : anonymous types*/
        var ann = new { Id = "123", Name = "Roy" };
        var annVariable = Utility.GetNameAndValue(() => ann);
        string annVariableName = annVariable.Item1;
        var annVariableValue = annVariable.Item2;

        /*Test : Enum tyoes*/
        Active isActive = Active.Yes;
        Tuple<string, Active> isActiveVariable = Utility.GetNameAndValue(() => isActive);
        string isActiveVariableName = isActiveVariable.Item1;
        Active isActiveVariableValue = isActiveVariable.Item2;
    }
2
Dipon Roy

Làm cái này

var myVariable = 123;
myVariable.Named(() => myVariable);
var name = myVariable.Name();
// use name how you like

hoặc đặt tên bằng mã bằng tay

var myVariable = 123.Named("my variable");
var name = myVariable.Name();

sử dụng lớp học này

public static class ObjectInstanceExtensions
{
    private static Dictionary<object, string> namedInstances = new Dictionary<object, string>();

    public static void Named<T>(this T instance, Expression<Func<T>> expressionContainingOnlyYourInstance)
    {
        var name = ((MemberExpression)expressionContainingOnlyYourInstance.Body).Member.Name;
        instance.Named(name);            
    }

    public static T Named<T>(this T instance, string named)
    {
        if (namedInstances.ContainsKey(instance)) namedInstances[instance] = named;
        else namedInstances.Add(instance, named);
        return instance;
    }        

    public static string Name<T>(this T instance)
    {
        if (namedInstances.ContainsKey(instance)) return namedInstances[instance];
        throw new NotImplementedException("object has not been named");
    }        
}

Mã thử nghiệm và thanh lịch nhất tôi có thể đưa ra.

1
kernowcode

GateKiller, có gì sai với cách giải quyết của tôi? Bạn có thể viết lại chức năng của mình một cách tầm thường để sử dụng nó (Tôi đã tự do cải thiện chức năng một cách nhanh chóng):

static string sMessages(Expression<Func<List<string>>> aMessages) {
    var messages = aMessages.Compile()();

    if (messages.Count == 0) {
        return "";
    }

    StringBuilder ret = new StringBuilder();
    string sType = ((MemberExpression)aMessages.Body).Member.Name;

    ret.AppendFormat("<p class=\"{0}\">", sType);
    foreach (string msg in messages) {
        ret.Append(msg);
        ret.Append("<br />");
    }
    ret.Append("</p>");
    return ret.ToString();
}

Gọi nó như thế này:

var errors = new List<string>() { "Hi", "foo" };
var ret = sMessages(() => errors);
0
Konrad Rudolph

Cảm ơn vì tất cả những phản hồi. Tôi đoán tôi sẽ phải đi với những gì tôi đang làm bây giờ.

Đối với những người muốn biết lý do tại sao tôi hỏi câu hỏi trên. Tôi có chức năng sau:

string sMessages(ArrayList aMessages, String sType) {
    string sReturn = String.Empty;
    if (aMessages.Count > 0) {
        sReturn += "<p class=\"" + sType + "\">";
        for (int i = 0; i < aMessages.Count; i++) {
            sReturn += aMessages[i] + "<br />";
        }
        sReturn += "</p>";
    }
    return sReturn;
}

Tôi gửi cho nó một mảng các thông báo lỗi và một lớp css sau đó được trả về dưới dạng chuỗi cho một trang web.

Mỗi lần tôi gọi hàm này, tôi phải xác định sType. Cái gì đó như:

output += sMessages(aErrors, "errors");

Như bạn có thể thấy, các biến của tôi được gọi là aErrors và lớp css của tôi được gọi là lỗi. Tôi đã hy vọng sự lạnh lùng của mình có thể tìm ra lớp nào sẽ sử dụng dựa trên tên biến tôi đã gửi nó.

Một lần nữa, cảm ơn tất cả các câu trả lời.

0
GateKiller

Không. Tham chiếu đến biến chuỗi của bạn được chuyển đến chức năng - không có bất kỳ metadeta vốn có nào về nó. Ngay cả sự phản chiếu cũng sẽ không đưa bạn ra khỏi khu rừng ở đây - làm việc ngược từ một loại tham chiếu duy nhất không giúp bạn có đủ thông tin để làm những gì bạn cần làm.

Tốt hơn là quay trở lại bảng vẽ trên này!

rp

0
rp.

Bạn có thể sử dụng sự phản chiếu để có được tất cả các thuộc tính của một đối tượng, hơn là lặp qua nó và lấy giá trị của thuộc tính trong đó tên (của thuộc tính) khớp với tham số được truyền vào.

0
Adam Vigh