c# - Lambda এক্সপ্রেশন থেকে সম্পত্তি নাম উদ্ধার করা হচ্ছে




linq expression-trees (14)

একটি Lambda অভিব্যক্তি মাধ্যমে পাস যখন সম্পত্তি নাম পেতে একটি ভাল উপায় আছে কি? এখানে আমি বর্তমানে কি আছে।

যেমন।

GetSortingInfo<User>(u => u.UserId);

এটি একটি স্ট্রিং ছিল যখন শুধুমাত্র এটি একটি memberexpression হিসাবে ঢালাই দ্বারা কাজ করে। কারণ সকল বৈশিষ্ট্য স্ট্রিংগুলি নয় যা আমাকে বস্তুটি ব্যবহার করতে হয়েছিল তবে তার জন্য এটি একটি অ্যারেপ্যাক্সেশন প্রদান করবে।

public static RouteValueDictionary GetInfo<T>(this HtmlHelper html, 
    Expression<Func<T, object>> action) where T : class
{
    var expression = GetMemberInfo(action);
    string name = expression.Member.Name;

    return GetInfo(html, name);
}

private static MemberExpression GetMemberInfo(Expression method)
{
    LambdaExpression lambda = method as LambdaExpression;
    if (lambda == null)
        throw new ArgumentNullException("method");

    MemberExpression memberExpr = null;

    if (lambda.Body.NodeType == ExpressionType.Convert)
    {
        memberExpr = 
            ((UnaryExpression)lambda.Body).Operand as MemberExpression;
    }
    else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
    {
        memberExpr = lambda.Body as MemberExpression;
    }

    if (memberExpr == null)
        throw new ArgumentException("method");

    return memberExpr;
}

.NET 4.0 এর সাথে শুরু করার জন্য আপনি ExpressionVisitor বৈশিষ্ট্যগুলি সন্ধান করতে ব্যবহার করতে পারেন:

class ExprVisitor : ExpressionVisitor {
    public bool IsFound { get; private set; }
    public string MemberName { get; private set; }
    public Type MemberType { get; private set; }
    protected override Expression VisitMember(MemberExpression node) {
        if (!IsFound && node.Member.MemberType == MemberTypes.Property) {
            IsFound = true;
            MemberName = node.Member.Name;
            MemberType = node.Type;
        }
        return base.VisitMember(node);
    }
}

আপনি এই দর্শকের কিভাবে ব্যবহার করেন:

var visitor = new ExprVisitor();
visitor.Visit(expr);
if (visitor.IsFound) {
    Console.WriteLine("First property in the expression tree: Name={0}, Type={1}", visitor.MemberName, visitor.MemberType.FullName);
} else {
    Console.WriteLine("No properties found.");
}

আচ্ছা, কল করার দরকার নাই। .Name.ToString() , তবে বিস্তৃতভাবে এটি সম্পর্কে, হ্যাঁ। x.Foo.Bar কে "Foo", "Bar", বা ব্যতিক্রমটি ফেরত দিতে হবে তা x.Foo.Bar আপনাকে বিবেচনা করতে হবে - অর্থাত্ আপনাকে x.Foo.Bar পুনরাবৃত্তি করতে হবে।

(মন্তব্য পুনরায় করুন) নমনীয় বাছাই আরও জন্য, here


আমি একই জিনিস সঙ্গে প্রায় বাজানো ছিল এবং এই কাজ। এটি সম্পূর্ণরূপে পরীক্ষা করা হয় না তবে এটি মান সমস্যাগুলির সাথে সমস্যাটি পরিচালনা করে বলে মনে হয় (আপনি যে অনিয়মিত সমস্যাটি চালাতে পেরেছেন)

public static string GetName(Expression<Func<object>> exp)
{
    MemberExpression body = exp.Body as MemberExpression;

    if (body == null) {
       UnaryExpression ubody = (UnaryExpression)exp.Body;
       body = ubody.Operand as MemberExpression;
    }

    return body.Member.Name;
}

আমি ক্যামেরনের উত্তরটি আপডেট করেছি, টাইপ করা ল্যাম্বা এক্সপ্রেশনগুলির পরিবর্তে কিছু নিরাপত্তা পরীক্ষা অন্তর্ভুক্ত করার জন্য:

PropertyInfo GetPropertyName<TSource, TProperty>(
Expression<Func<TSource, TProperty>> propertyLambda)
{
  var body = propertyLambda.Body;
  if (!(body is MemberExpression member)
    && !(body is UnaryExpression unary
      && (member = unary.Operand as MemberExpression) != null))
    throw new ArgumentException($"Expression '{propertyLambda}' " +
      "does not refer to a property.");

  if (!(member.Member is PropertyInfo propInfo))
    throw new ArgumentException($"Expression '{propertyLambda}' " +
      "refers to a field, not a property.");

  var type = typeof(TSource);
  if (!propInfo.DeclaringType.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
    throw new ArgumentException($"Expresion '{propertyLambda}' " + 
      "refers to a property that is not from type '{type}'.");

  return propInfo;
}

আমি নিম্নলিখিত কাজ করছি:

এখানে পদ্ধতি, POCOS, তালিকা, স্ট্রিং, তারিখকাল, এবং প্রাইমাইটিভগুলির বৈশিষ্ট্যগুলির বিরুদ্ধে পরীক্ষা করা হয়েছে:

public string GetFieldName<T>(Expression<Func<T, object>> excludedField){
    if (excludedField.Body.NodeType == ExpressionType.Convert) //Value type
            {
                UnaryExpression body = excludedField.Body as UnaryExpression;
                MemberExpression operand = body?.Operand as MemberExpression;
                return operand?.Member.Name;
            }
            else if(excludedField.Body.NodeType == ExpressionType.MemberAccess) //Ref type
            {
                MemberExpression body = excludedField.Body as MemberExpression;
                return body?.Member.Name;
            }
}

আমি সম্প্রতি একটি টাইপ নিরাপদ OnPropertyChanged পদ্ধতি করতে একটি খুব অনুরূপ জিনিস করেনি।

এখানে একটি পদ্ধতি যা অভিব্যক্তিটির জন্য PropertyInfo অবজেক্টটি ফিরিয়ে দেবে। অভিব্যক্তি একটি সম্পত্তি না হলে এটি একটি ব্যতিক্রম ছুড়ে ফেলে।

public PropertyInfo GetPropertyInfo<TSource, TProperty>(
    TSource source,
    Expression<Func<TSource, TProperty>> propertyLambda)
{
    Type type = typeof(TSource);

    MemberExpression member = propertyLambda.Body as MemberExpression;
    if (member == null)
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a method, not a property.",
            propertyLambda.ToString()));

    PropertyInfo propInfo = member.Member as PropertyInfo;
    if (propInfo == null)
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a field, not a property.",
            propertyLambda.ToString()));

    if (type != propInfo.ReflectedType &&
        !type.IsSubclassOf(propInfo.ReflectedType))
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a property that is not from type {1}.",
            propertyLambda.ToString(),
            type));

    return propInfo;
}

source প্যারামিটার ব্যবহার করা হয় তাই কম্পাইলার পদ্ধতি কল টাইপ পরিমাপ করতে পারেন। আপনি নিম্নলিখিত করতে পারেন

var propertyInfo = GetPropertyInfo(someUserObject, u => u.UserID);

উত্স এবং সম্পত্তি দৃঢ়ভাবে টাইপ করা এবং ল্যাম্বারের জন্য ইনপুটটিকে নিখুঁতভাবে বোঝার জন্য আমি অন্য উপায়টি খুঁজে পেয়েছি। নিশ্চিত না হলে এটি সঠিক পরিভাষা কিন্তু এখানে ফলাফল।

public static RouteValueDictionary GetInfo<T,P>(this HtmlHelper html, Expression<Func<T, P>> action) where T : class
{
    var expression = (MemberExpression)action.Body;
    string name = expression.Member.Name;

    return GetInfo(html, name);
}

এবং তারপর তাই এটি কল।

GetInfo((User u) => u.UserId);

এবং voila এটা কাজ করে।
সবাইকে ধন্যবাদ.


এখন সি # 6 তে আপনি কেবলমাত্র এই nameof(User.UserId) মত ব্যবহারকারীর নাম ব্যবহার করতে পারেন nameof(User.UserId)

যার মধ্যে অনেকগুলি সুবিধা রয়েছে, এটি হ'ল কম্পাইলাইল সময় , রানটাইম নয়।

https://msdn.microsoft.com/en-us/magazine/dn802602.aspx


এটি অন্য একটি উত্তর:

public static string GetPropertyName<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
                                                                      Expression<Func<TModel, TProperty>> expression)
    {
        var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

        return metaData.PropertyName;
    }

ক্ষেত্র / বৈশিষ্ট্য / সূচক / পদ্ধতি / এক্সটেনশান পদ্ধতি / স্ট্রাক / ক্লাস / ইন্টারফেস / প্রতিনিধি / অ্যারের প্রতিনিধির স্ট্রিং নাম পেতে এটি একটি সাধারণ বাস্তবায়ন। আমি স্ট্যাটিক / দৃষ্টান্ত এবং অ জেনেরিক / জেনেরিক বৈকল্পিক সমন্বয় সঙ্গে পরীক্ষা করেছেন।

//involves recursion
public static string GetMemberName(this LambdaExpression memberSelector)
{
    Func<Expression, string> nameSelector = null;  //recursive func
    nameSelector = e => //or move the entire thing to a separate recursive method
    {
        switch (e.NodeType)
        {
            case ExpressionType.Parameter:
                return ((ParameterExpression)e).Name;
            case ExpressionType.MemberAccess:
                return ((MemberExpression)e).Member.Name;
            case ExpressionType.Call:
                return ((MethodCallExpression)e).Method.Name;
            case ExpressionType.Convert:
            case ExpressionType.ConvertChecked:
                return nameSelector(((UnaryExpression)e).Operand);
            case ExpressionType.Invoke:
                return nameSelector(((InvocationExpression)e).Expression);
            case ExpressionType.ArrayLength:
                return "Length";
            default:
                throw new Exception("not a proper member selector");
        }
    };

    return nameSelector(memberSelector.Body);
}

লুপ খুব সহজে এই জিনিসটি লেখা যেতে পারে:

//iteration based
public static string GetMemberName(this LambdaExpression memberSelector)
{
    var currentExpression = memberSelector.Body;

    while (true)
    {
        switch (currentExpression.NodeType)
        {
            case ExpressionType.Parameter:
                return ((ParameterExpression)currentExpression).Name;
            case ExpressionType.MemberAccess:
                return ((MemberExpression)currentExpression).Member.Name;
            case ExpressionType.Call:
                return ((MethodCallExpression)currentExpression).Method.Name;
            case ExpressionType.Convert:
            case ExpressionType.ConvertChecked:
                currentExpression = ((UnaryExpression)currentExpression).Operand;
                break;
            case ExpressionType.Invoke:
                currentExpression = ((InvocationExpression)currentExpression).Expression;
                break;
            case ExpressionType.ArrayLength:
                return "Length";
            default:
                throw new Exception("not a proper member selector");
        }
    }
}

আমি recursive পদ্ধতির পছন্দ, যদিও দ্বিতীয় এক পড়তে সহজ হতে পারে। এক মত এটি কল করতে পারেন:

someExpr = x => x.Property.ExtensionMethod()[0]; //or
someExpr = x => Static.Method().Field; //or
someExpr = x => VoidMethod(); //or
someExpr = () => localVariable; //or
someExpr = x => x; //or
someExpr = x => (Type)x; //or
someExpr = () => Array[0].Delegate(null); //etc

string name = someExpr.GetMemberName();

শেষ সদস্য মুদ্রণ।

বিঃদ্রঃ:

  1. ABC মত শৃঙ্খলা অভিব্যক্তি ক্ষেত্রে, "সি" ফিরে।

  2. এটি কনস্টেক্স, অ্যারে ইন্ডেক্সারস বা enum এস (সমস্ত ক্ষেত্রে ঢোকানো অসম্ভব) দিয়ে কাজ করে না।


যদি আপনি বহুগুণ ক্ষেত্রগুলি পেতে চান তবে আমি এই ফাংশনটি ছেড়ে দিই:

/// <summary>
    /// Get properties separated by , (Ex: to invoke 'd => new { d.FirstName, d.LastName }')
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="exp"></param>
    /// <returns></returns>
    public static string GetFields<T>(Expression<Func<T, object>> exp)
    {
        MemberExpression body = exp.Body as MemberExpression;
        var fields = new List<string>();
        if (body == null)
        {
            NewExpression ubody = exp.Body as NewExpression;
            if (ubody != null)
                foreach (var arg in ubody.Arguments)
                {
                    fields.Add((arg as MemberExpression).Member.Name);
                }
        }

        return string.Join(",", fields);
    }

সি # 7 প্যাটার্ন মিলিং সঙ্গে:

public static string GetMemberName<T>(this Expression<T> expression)
{
    switch (expression.Body)
    {
        case MemberExpression m:
            return m.Member.Name;
        case UnaryExpression u when u.Operand is MemberExpression m:
            return m.Member.Name;
        default:
            throw new NotImplementedException(expression.GetType().ToString());
    }
}

উদাহরণ:

public static RouteValueDictionary GetInfo<T>(this HtmlHelper html, 
    Expression<Func<T, object>> action) where T : class
{
    var name = action.GetMemberName();
    return GetInfo(html, name);
}

এই উত্তর বন্ধ PropertyInfo পেতে অন্য উপায় এখানে । এটি একটি বস্তুর উদাহরণ জন্য প্রয়োজন নির্মূল করে।

/// <summary>
/// Get metadata of property referenced by expression. Type constrained.
/// </summary>
public static PropertyInfo GetPropertyInfo<TSource, TProperty>(Expression<Func<TSource, TProperty>> propertyLambda)
{
    return GetPropertyInfo((LambdaExpression) propertyLambda);
}

/// <summary>
/// Get metadata of property referenced by expression.
/// </summary>
public static PropertyInfo GetPropertyInfo(LambdaExpression propertyLambda)
{
    // https://.com/questions/671968/retrieving-property-name-from-lambda-expression
    MemberExpression member = propertyLambda.Body as MemberExpression;
    if (member == null)
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a method, not a property.",
            propertyLambda.ToString()));

    PropertyInfo propInfo = member.Member as PropertyInfo;
    if (propInfo == null)
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a field, not a property.",
            propertyLambda.ToString()));

    if(propertyLambda.Parameters.Count() == 0)
        throw new ArgumentException(String.Format(
            "Expression '{0}' does not have any parameters. A property expression needs to have at least 1 parameter.",
            propertyLambda.ToString()));

    var type = propertyLambda.Parameters[0].Type;
    if (type != propInfo.ReflectedType &&
        !type.IsSubclassOf(propInfo.ReflectedType))
        throw new ArgumentException(String.Format(
            "Expression '{0}' refers to a property that is not from type {1}.",
            propertyLambda.ToString(),
            type));
    return propInfo;
}

এটি এভাবে বলা যেতে পারে:

var propertyInfo = GetPropertyInfo((User u) => u.UserID);

public string GetName<TSource, TField>(Expression<Func<TSource, TField>> Field)
{
    return (Field.Body as MemberExpression ?? ((UnaryExpression)Field.Body).Operand as MemberExpression).Member.Name;
}

এই সদস্য এবং unary এক্সপ্রেশন পরিচালনা করে। পার্থক্য হচ্ছে আপনি যদি UnaryExpression তবে আপনার অভিব্যক্তি একটি মান টাইপ উপস্থাপন করে এবং যদি আপনার অভিব্যক্তি একটি রেফারেন্স টাইপ উপস্থাপিত করে তবে আপনি MemberExpression পাবেন। সবকিছু একটি বস্তুর নিক্ষেপ করা যেতে পারে, কিন্তু মান টাইপ বক্স করা আবশ্যক। কেন UnaryExpression বিদ্যমান। Reference.

পাঠযোগ্যতা (@ জোয়েন) এর পক্ষে, এখানে একটি বিস্তৃত সমতুল্য:

public string GetName<TSource, TField>(Expression<Func<TSource, TField>> Field)
{
    if (object.Equals(Field, null))
    {
        throw new NullReferenceException("Field is required");
    }

    MemberExpression expr = null;

    if (Field.Body is MemberExpression)
    {
        expr = (MemberExpression)Field.Body;
    }
    else if (Field.Body is UnaryExpression)
    {
        expr = (MemberExpression)((UnaryExpression)Field.Body).Operand;
    }
    else
    {
        const string Format = "Expression '{0}' not supported.";
        string message = string.Format(Format, Field);

        throw new ArgumentException(message, "Field");
    }

    return expr.Member.Name;
}




expression-trees