ল্যাম্বডা এক্সপ্রেশন থেকে সম্পত্তির নাম পুনরুদ্ধার করা


511

ল্যাম্বডা এক্সপ্রেশন দিয়ে যাওয়ার পরে সম্পত্তিটির নাম পাওয়ার কী আরও ভাল উপায় আছে? আমার কাছে বর্তমানে যা আছে তা এখানে।

যেমন।

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

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

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

ভাল কোড হিসাবে ভাল? আমি তাই মনে করি না. টাইপচেকিংটি কেবল সামগ্রিক অভিব্যক্তি পর্যন্ত প্রসারিত, সুতরাং রানটাইমের সময় আপনার সত্যিকারের চেকগুলি দরকার। :(
মাইকেলগিজি

হ্যাঁ ... আমি ভাবছিলাম যে এটি করার আরও ভাল উপায় আছে কিনা, কারণ এটি আমার কাছে কিছুটা ক্ষুধা অনুভূত হয়েছিল। তবে যদি তা হয় তবে শীতল। ধন্যবাদ।
স্কটিমে

আমি আপনার মন্তব্য আবার আপডেট; তবে একটি স্ট্রিং পেতে ল্যাম্বডা ব্যবহার করে যাতে আপনি ডায়নামিক লিনকুই ব্যবহার করতে পারেন আমাকে পিছন দিকে জিনিসগুলি হিসাবে আঘাত করে ... আপনি যদি ল্যাম্বডা ব্যবহার করেন, একটি ল্যাম্বডা ব্যবহার করুন ;- পি আপনাকে পুরো পদক্ষেপটি এক পদক্ষেপে করতে হবে না - আপনি "নিয়মিত / ল্যাম্বডা" অর্ডারবাই, "গতিশীল লিনকুই / স্ট্রিং" কোথায় ইত্যাদি ব্যবহার করতে পারেন
মার্ক গ্র্যাভেল


4
প্রত্যেকের কাছে একটি নোট: MemberExpressionকেবলমাত্র নামটির জন্য এখানে তালিকাবদ্ধ পদ্ধতির ব্যবহার করুন , প্রকৃত নামটি না পাওয়ার জন্য MemberInfo, কারণ MemberInfoপ্রত্যাবর্তিত কিছু নির্দিষ্ট "ডারভেড: বেস" দৃশ্যে প্রতিফলিত ধরণের হওয়ার নিশ্চয়তা নেই। দেখুন ল্যামডা প্রকাশ নয়-ফিরে-এর প্রত্যাশিত-memberinfo । একবার আমাকে ফাঁদে ফেলেছে। গৃহীত উত্তরও এ থেকে ভোগে।
নওফাল

উত্তর:


350

একটি প্রকার নিরাপদ অনপোপার্টিচেনজড পদ্ধতি তৈরি করতে আমি সম্প্রতি খুব অনুরূপ একটি কাজ করেছি।

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

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

6
সেখানে টিএসোর্স সম্পর্কিত সর্বশেষ চেকটি কেন? ল্যাম্বদা দৃ strongly়ভাবে টাইপ করা হয়েছে তাই আমি এটি প্রয়োজনীয় মনে করি না।
শুভনোমাদ

16
এছাড়াও, ২০১২ পর্যন্ত, টাইপ অনুমানটি উত্স প্যারামিটার ছাড়াই দুর্দান্ত কাজ করে works
শুভনোমাদ

4
@ হ্যাপি নোমাদ একটি তৃতীয় ধরণের উদাহরণ হিসাবে সদস্য হিসাবে রয়েছে এমন একটি বস্তুর কল্পনা করুন। u => u.OtherType.OtherTypesPropertyএমন কেস তৈরি করবে যে সর্বশেষ বিবৃতিটি অনুসন্ধান করছে।
জোশপুরি

5
শেষ যদি বিবৃতিটি হওয়া উচিত: if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType) && !propInfo.ReflectedType.IsAssignableFrom(type))ইন্টারফেসের জন্যও অনুমতি দেওয়া allow
গ্রাহাম কিং

8
@ গ্রেকিং কি ঠিক সেইরকম হবে না if(!propInfo.ReflectedType.IsAssignableFrom(type))?
কনেল

192

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

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 এটি কাজ করে।
সবাইকে ধন্যবাদ.


4
এই সমাধানটি কিছুটা আপডেট হওয়া উচিত। দয়া করে নীচের নিবন্ধটি দেখুন - এখানে একটি লিঙ্কটি রয়েছে
পাভেল সারম্যাক

1
আপনি যদি এএসপি.নেট এমভিসি করেন এবং কেবলমাত্র ইউআই স্তর (এইচটিএমএলহেল্পার) করেন তবে এটি কেবলমাত্র একটি বিকল্প।
মার্চ

3
সি # 6.0 থেকে শুরু করে আপনি ব্যবহার করতে পারেনGetInfo(nameof(u.UserId))
ভ্লাদিস্লাভ

1
: নেট কোর আমি এটি ব্যবহার করতে হয়েছেvar name = ((MemberExpression) ((UnaryExpression) accessor.Body).Operand).Member.Name
ফাল্ক

146

আমি একই জিনিসটি নিয়ে চারপাশে খেলছিলাম এবং এটির কাজ করেছি। এটি পুরোপুরি পরীক্ষিত নয় তবে মানটির ধরণের সমস্যাটি (যা আপনার মধ্যে ছড়িয়ে পড়েছে এমন unaryexpression ইস্যু) দিয়ে পরিচালনা করছে বলে মনে হচ্ছে

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

2
এই সম্প্রতি চেষ্টা (থেকে আরেকটি প্রশ্ন ), খুঁজে পাওয়া যায় নি এটা subproperties হ্যান্ডেল করে না: o => o.Thing1.Thing2ফিরে আসবে Thing2না, Thing1.Thing2, আপনি EntityFramework এটি ব্যবহার করার চেষ্টা করছেন, তাহলে ভুল রয়েছে
drzaus

1
একে (ফিল্ড.বডিটি ইউনারিএক্সপ্রেসন? ((ইউনিারি এক্সপ্রেসন) ফিল্ড.বডি) ।অপ্রেন্ড: ফিল্ড.বডি) মেমপ্রেস এক্সপ্রেশন হিসাবে

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

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

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

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

@ ফ্লেম, আমি পাঠযোগ্যতার জন্য <টি ফিল্ড> বাদ দিচ্ছি, কোনও সমস্যা আছে। ল্যাম্বডএএক্সপ্রেসনস.গেটনাম <ব্যাসকেট> (এম => মি। কোয়ান্টিটি)
সোরেন

1
@ সোরেন আমি নিশ্চিত যে আমার চেয়ে আরও বেশি কেউ এই বিষয়ে পরামর্শ দিতে পারে যে আপনি নিজের কোডটি অপ্রয়োজনীয় বক্সিং / আনবক্সিংয়ের সম্ভাব্যতা পর্যন্ত প্রকাশ করছেন যখন মান ধরণের অভিব্যক্তিগুলি পাস করার সময়, তবে এই পদ্ধতিতে প্রকাশটি কখনই সংকলন ও মূল্যায়ন হয় না, এটি সম্ভবত কোনও সমস্যা নয়।
পল ফ্লেমিং

29

সি # 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);
}

[আপডেট] সি # 8 প্যাটার্নের মিল:

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


20

ক্ষেত্র / বৈশিষ্ট্য / সূচক / পদ্ধতি / সম্প্রসারণ পদ্ধতি / কাঠামো / শ্রেণি / ইন্টারফেস / প্রতিনিধি / অ্যারের প্রতিনিধিদের স্ট্রিং নাম পেতে এটি একটি সাধারণ বাস্তবায়ন। আমি স্থিতিশীল / উদাহরণ এবং নন-জেনেরিক / জেনেরিক রূপগুলির সংমিশ্রণগুলি দিয়ে পরীক্ষা করেছি tested

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

এই জিনিসটি একটি সাধারণ whileলুপেও লেখা যেতে পারে :

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

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

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. শৃঙ্খলিত মত প্রকাশের ক্ষেত্রে A.B.C"সি" ফিরিয়ে দেওয়া হয়

  2. এটি consts, অ্যারে সূচক বা enumগুলি (সমস্ত ক্ষেত্রে কভার করা অসম্ভব) দিয়ে কাজ করে না ।


19

এটি যখন প্রান্তের ক্ষেত্রে আসে তখন Array। দৈর্ঘ্য। 'দৈর্ঘ্য' সম্পত্তি হিসাবে প্রকাশিত হওয়ার পরে, আপনি এটি পূর্ব প্রস্তাবিত কোনও সমাধানে ব্যবহার করতে পারবেন না।

using Contract = System.Diagnostics.Contracts.Contract;
using Exprs = System.Linq.Expressions;

static string PropertyNameFromMemberExpr(Exprs.MemberExpression expr)
{
    return expr.Member.Name;
}

static string PropertyNameFromUnaryExpr(Exprs.UnaryExpression expr)
{
    if (expr.NodeType == Exprs.ExpressionType.ArrayLength)
        return "Length";

    var mem_expr = expr.Operand as Exprs.MemberExpression;

    return PropertyNameFromMemberExpr(mem_expr);
}

static string PropertyNameFromLambdaExpr(Exprs.LambdaExpression expr)
{
         if (expr.Body is Exprs.MemberExpression)   return PropertyNameFromMemberExpr(expr.Body as Exprs.MemberExpression);
    else if (expr.Body is Exprs.UnaryExpression)    return PropertyNameFromUnaryExpr(expr.Body as Exprs.UnaryExpression);

    throw new NotSupportedException();
}

public static string PropertyNameFromExpr<TProp>(Exprs.Expression<Func<TProp>> expr)
{
    Contract.Requires<ArgumentNullException>(expr != null);
    Contract.Requires<ArgumentException>(expr.Body is Exprs.MemberExpression || expr.Body is Exprs.UnaryExpression);

    return PropertyNameFromLambdaExpr(expr);
}

public static string PropertyNameFromExpr<T, TProp>(Exprs.Expression<Func<T, TProp>> expr)
{
    Contract.Requires<ArgumentNullException>(expr != null);
    Contract.Requires<ArgumentException>(expr.Body is Exprs.MemberExpression || expr.Body is Exprs.UnaryExpression);

    return PropertyNameFromLambdaExpr(expr);
}

এখন উদাহরণ ব্যবহার:

int[] someArray = new int[1];
Console.WriteLine(PropertyNameFromExpr( () => someArray.Length ));

যদি অনুসন্ধান PropertyNameFromUnaryExprনা করা হয় ArrayLength, তবে "কিছুআরে" কনসোলে মুদ্রিত হবে (সংকলকটি ডাবগের ক্ষেত্রেও অপ্টিমাইজেশন হিসাবে, ব্যাকিং লেংথ ফিল্ডে সরাসরি অ্যাক্সেস তৈরি করে বলে মনে হচ্ছে ) এইভাবে বিশেষ ক্ষেত্রে)।


16

ক্যামেরনের প্রস্তাবিত পদ্ধতির আপডেট এখানে রয়েছে । প্রথম প্যারামিটারের প্রয়োজন নেই।

public PropertyInfo GetPropertyInfo<TSource, TProperty>(
    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(
            "Expresion '{0}' refers to a property that is not from type {1}.",
            propertyLambda.ToString(),
            type));

    return propInfo;
}

আপনি নিম্নলিখিতটি করতে পারেন:

var propertyInfo = GetPropertyInfo<SomeType>(u => u.UserID);
var propertyInfo = GetPropertyInfo((SomeType u) => u.UserID);

সম্প্রসারণ পদ্ধতি:

public static PropertyInfo GetPropertyInfo<TSource, TProperty>(this TSource source,
    Expression<Func<TSource, TProperty>> propertyLambda) where TSource : class
{
    return GetPropertyInfo(propertyLambda);
}

public static string NameOfProperty<TSource, TProperty>(this TSource source,
    Expression<Func<TSource, TProperty>> propertyLambda) where TSource : class
{
    PropertyInfo prodInfo = GetPropertyInfo(propertyLambda);
    return prodInfo.Name;
}

আপনি পারেন:

SomeType someInstance = null;
string propName = someInstance.NameOfProperty(i => i.Length);
PropertyInfo propInfo = someInstance.GetPropertyInfo(i => i.Length);

না তিনি কোনও uপ্রকার হিসাবে অনুমান করবেন না , তিনি তা করতে পারবেন না কারণ অনুমান করার মতো টাইপ নেই। আপনি যা করতে পারেন তা হলGetPropertyInfo<SomeType>(u => u.UserID)
লুকাস

14

আমি পেয়েছি কিছু যে প্রস্তাব উত্তর যা নিচে কসরত MemberExpression/ UnaryExpressionনেস্টেড না ক্যাপচার / subproperties।

প্রাক্তন) পরিবর্তে o => o.Thing1.Thing2রিটার্ন ।Thing1Thing1.Thing2

আপনি যদি এনটিটি ফ্রেমওয়ার্কের সাথে কাজ করার চেষ্টা করছেন তবে এই পার্থক্যটি গুরুত্বপূর্ণ DbSet.Include(...)

আমি খুঁজে পেয়েছি যে কেবল পার্সিং করা ঠিক Expression.ToString()মনে হয় কাজ করে, এবং তুলনামূলক দ্রুত। আমি এটি UnaryExpressionসংস্করণটির সাথে তুলনা করেছি এবং এমনকি ToStringএটি বন্ধ করে দিচ্ছিMember/UnaryExpression এটি দ্রুত হয়েছে কিনা তা দেখার পার্থক্যটি নগণ্য। দয়া করে আমাকে সংশোধন করুন যদি এটি কোনও ভয়ঙ্কর ধারণা।

এক্সটেনশন পদ্ধতি

/// <summary>
/// Given an expression, extract the listed property name; similar to reflection but with familiar LINQ+lambdas.  Technique @via https://stackoverflow.com/a/16647343/1037948
/// </summary>
/// <remarks>Cheats and uses the tostring output -- Should consult performance differences</remarks>
/// <typeparam name="TModel">the model type to extract property names</typeparam>
/// <typeparam name="TValue">the value type of the expected property</typeparam>
/// <param name="propertySelector">expression that just selects a model property to be turned into a string</param>
/// <param name="delimiter">Expression toString delimiter to split from lambda param</param>
/// <param name="endTrim">Sometimes the Expression toString contains a method call, something like "Convert(x)", so we need to strip the closing part from the end</param>
/// <returns>indicated property name</returns>
public static string GetPropertyName<TModel, TValue>(this Expression<Func<TModel, TValue>> propertySelector, char delimiter = '.', char endTrim = ')') {

    var asString = propertySelector.ToString(); // gives you: "o => o.Whatever"
    var firstDelim = asString.IndexOf(delimiter); // make sure there is a beginning property indicator; the "." in "o.Whatever" -- this may not be necessary?

    return firstDelim < 0
        ? asString
        : asString.Substring(firstDelim+1).TrimEnd(endTrim);
}//--   fn  GetPropertyNameExtended

(ডিলিমিটারের জন্য চেক করা এমনকি ওভারকিল হতে পারে)

ডেমো (লিনকপ্যাড)

বিক্ষোভ + তুলনা কোড - https://gist.github.com/zaus/6992590


1
+ 1 খুব আকর্ষণীয়। আপনি কি নিজের কোডে এই পদ্ধতিটি ব্যবহার চালিয়ে গেছেন? এটা ঠিক কাজ করে? আপনি কোন প্রান্ত ক্ষেত্রে আবিষ্কার করেছেন?
বেনিয়ামিন গালে

আমি আপনার ধারণা দেখতে ব্যর্থ। আপনি যে উত্তরটি যুক্ত করেছেন o => o.Thing1.Thing2তাতে গিয়ে Thing1আপনি যা বলছেন তেমন ফিরবে না Thing2। আসলে আপনার উত্তরটি এমন কিছু দেয় Thing1.Thing2যা পছন্দসই বা নাও পারে।
নওফাল

কেস korman সতর্কতা সাথে কাজ করে না: stackoverflow.com/a/11006147/661933 । হ্যাক এড়ানোর জন্য সর্বদা ভাল।
নওফাল

@ নওফাল # 1 - আসল সমস্যাটি হ'ল আপনি চান Thing1.Thing2 , কখনই নয় Thing1। আমি বললাম Thing2মানে মান এর o.Thing1.Thing2, যা সম্পৃক্ত বিন্দু। আমি সেই উদ্দেশ্যটি প্রতিফলিত করতে উত্তর আপডেট করব।
ড্রজাউস

@ ড্রজাউস দুঃখিত আমি এখনও আপনাকে পাইনি। সত্যই বোঝার চেষ্টা করছি। আপনি কেন বলবেন যে এখানে অন্যান্য উত্তরগুলি ফিরে আসে Thing1? আমি মনে করি না যে এটি একেবারে পুনরায় ফিরে আসে।
নওফাল

6

আমি প্রাক # # 6 টি প্রকল্পের জন্য একটি এক্সটেনশন পদ্ধতি এবং সি # 6 টার্গেটকারীদের নাম ()) ব্যবহার করছি।

public static class MiscExtentions
{
    public static string NameOf<TModel, TProperty>(this object @object, Expression<Func<TModel, TProperty>> propertyExpression)
    {
        var expression = propertyExpression.Body as MemberExpression;
        if (expression == null)
        {
            throw new ArgumentException("Expression is not a property.");
        }

        return expression.Member.Name;
    }
}

এবং আমি এটিকে কল করি:

public class MyClass 
{
    public int Property1 { get; set; }
    public string Property2 { get; set; }
    public int[] Property3 { get; set; }
    public Subclass Property4 { get; set; }
    public Subclass[] Property5 { get; set; }
}

public class Subclass
{
    public int PropertyA { get; set; }
    public string PropertyB { get; set; }
}

// result is Property1
this.NameOf((MyClass o) => o.Property1);
// result is Property2
this.NameOf((MyClass o) => o.Property2);
// result is Property3
this.NameOf((MyClass o) => o.Property3);
// result is Property4
this.NameOf((MyClass o) => o.Property4);
// result is PropertyB
this.NameOf((MyClass o) => o.Property4.PropertyB);
// result is Property5
this.NameOf((MyClass o) => o.Property5);

এটি উভয় ক্ষেত্র এবং বৈশিষ্ট্যগুলির সাথে সূক্ষ্মভাবে কাজ করে।


5

ঠিক আছে, কল করার দরকার নেই .Name.ToString(), তবে এটি সম্পর্কে হ'ল, এটি মোটামুটি। আপনার কেবলমাত্র বিবেচনার প্রয়োজন হতে পারে হ'ল x.Foo.Bar"ফু", "বার", বা একটি ব্যতিক্রম - কি আপনাকে আদৌ পুনরুক্ত করা দরকার।

নমনীয় বাছাইয়ের বিষয়ে আরও তথ্যের জন্য (মন্তব্য করুন), এখানে দেখুন


হ্যাঁ ... এটি কেবলমাত্র প্রথম স্তরের জিনিস, বাছাইকরণ কলাম লিঙ্ক তৈরির জন্য ব্যবহৃত। যেমন। যদি আমার একটি মডেল থাকে এবং আমি কলামের নামটি বাছাই করতে চাই তবে আমি তার নামটির জন্য দৃ get়ভাবে টাইপ করা লিংকটি ব্যবহার করতে পারি যার জন্য গতিশীল লিংকের গাভী থাকবে না। চিয়ার্স।
স্কটিমে

ToStringঅযৌক্তিক প্রকাশের জন্য কুৎসিত ফলাফল দেওয়া উচিত।
নওফাল

3

ডিফল্ট পদ্ধতিটি কেবল একটি স্ট্রিং গ্রহণ করে, আমি একটি প্রকার সুরক্ষিত পদ্ধতিতে পরিবর্তিত হিসাবে বৈশিষ্ট্যগুলি (সত্তা ফ্রেমওয়ার্ক পোকো ক্লাসগুলির) ফ্ল্যাগ করতে সক্ষম হতে অবজেক্টস্টেটএন্ট্রিতে একটি এক্সটেনশন পদ্ধতি তৈরি করেছি created সম্পত্তি থেকে নাম পাওয়ার আমার উপায় এখানে:

public static void SetModifiedProperty<T>(this System.Data.Objects.ObjectStateEntry state, Expression<Func<T>> action)
{
    var body = (MemberExpression)action.Body;
    string propertyName = body.Member.Name;

    state.SetModifiedProperty(propertyName);
}

3

আমি INotifyPropertyChangedনীচের পদ্ধতির অনুরূপ বাস্তবায়ন করেছি । এখানে বৈশিষ্ট্যগুলি নীচে প্রদর্শিত বেস ক্লাসে একটি অভিধানে সঞ্চিত আছে। উত্তরাধিকার ব্যবহার করা অবশ্যই সবসময় কাম্য নয়, তবে ভিউ মডেলগুলির জন্য আমি মনে করি এটি গ্রহণযোগ্য এবং ভিউ মডেল শ্রেণিতে খুব পরিষ্কার সম্পত্তি রেফারেন্স দেয়।

public class PhotoDetailsViewModel
    : PropertyChangedNotifierBase<PhotoDetailsViewModel>
{
    public bool IsLoading
    {
        get { return GetValue(x => x.IsLoading); }
        set { SetPropertyValue(x => x.IsLoading, value); }
    }

    public string PendingOperation
    {
        get { return GetValue(x => x.PendingOperation); }
        set { SetPropertyValue(x => x.PendingOperation, value); }
    }

    public PhotoViewModel Photo
    {
        get { return GetValue(x => x.Photo); }
        set { SetPropertyValue(x => x.Photo, value); }
    }
}

কিছুটা আরও জটিল বেস শ্রেণিটি নীচে দেখানো হয়েছে। এটি ল্যাম্বডা এক্সপ্রেশন থেকে সম্পত্তি নাম পর্যন্ত অনুবাদ পরিচালনা করে। মনে রাখবেন যে কেবলমাত্র নাম ব্যবহার করা হওয়ায় বৈশিষ্ট্যগুলি সত্যই সিউডো বৈশিষ্ট্য। তবে এটি ভিউ মডেলটিতে স্বচ্ছ এবং ভিউ মডেলের বৈশিষ্ট্যগুলির উল্লেখের জন্য প্রদর্শিত হবে।

public class PropertyChangedNotifierBase<T> : INotifyPropertyChanged
{
    readonly Dictionary<string, object> _properties = new Dictionary<string, object>();

    protected U GetValue<U>(Expression<Func<T, U>> property)
    {
        var propertyName = GetPropertyName(property);

        return GetValue<U>(propertyName);
    }

    private U GetValue<U>(string propertyName)
    {
        object value;

        if (!_properties.TryGetValue(propertyName, out value))
        {
            return default(U);
        }

        return (U)value;
    }

    protected void SetPropertyValue<U>(Expression<Func<T, U>> property, U value)
    {
        var propertyName = GetPropertyName(property);

        var oldValue = GetValue<U>(propertyName);

        if (Object.ReferenceEquals(oldValue, value))
        {
            return;
        }
        _properties[propertyName] = value;

        RaisePropertyChangedEvent(propertyName);
    }

    protected void RaisePropertyChangedEvent<U>(Expression<Func<T, U>> property)
    {
        var name = GetPropertyName(property);
        RaisePropertyChangedEvent(name);
    }

    protected void RaisePropertyChangedEvent(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private static string GetPropertyName<U>(Expression<Func<T, U>> property)
    {
        if (property == null)
        {
            throw new NullReferenceException("property");
        }

        var lambda = property as LambdaExpression;

        var memberAssignment = (MemberExpression) lambda.Body;
        return memberAssignment.Member.Name;
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

1
আপনি মূলত একটি সম্পত্তি ব্যাগ রক্ষণাবেক্ষণ করছেন। খারাপ নয়, তবে মডেল ক্লাসের গেটরস এবং সেটটারদের কাছ থেকে আসা কলগুলি খুব সহজ public bool IsLoading { get { return GetValue(MethodBase.GetCurrentMethod().Name); } set { SetPropertyValue(MethodBase.GetCurrentMethod().Name, value); } }। ধীর হতে পারে, তবে আরও সাধারণ এবং সোজা।
নওফাল

প্রকৃতপক্ষে একটি সাধারণ নির্ভরতা সম্পত্তি ব্যবস্থা বাস্তবায়ন করা শক্ত (তবে খুব কঠিন নয়) তবে উপরের প্রয়োগের তুলনায় আসলে আরও অনেক বেশি পারফরম্যান্ট।
ফেলিক্স কে।

3

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

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

1
ModelMetadataSystem.Web.Mvcনেমস্পেসে বিদ্যমান । সম্ভবত এটি সাধারণ ক্ষেত্রে উপযুক্ত নয়
asakura89

3

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

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

3
আপনি এই ব্যাখ্যা করতে যাচ্ছেন?

1

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

/// <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)
{
    // /programming/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);

1

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

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

1

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

1

এটি সর্বোত্তম হতে পারে

public static string GetPropertyName<TResult>(Expression<Func<TResult>> expr)
{
    var memberAccess = expr.Body as MemberExpression;
    var propertyInfo = memberAccess?.Member as PropertyInfo;
    var propertyName = propertyInfo?.Name;

    return propertyName;
}

0
static void Main(string[] args)
{
    var prop = GetPropertyInfo<MyDto>(_ => _.MyProperty);

    MyDto dto = new MyDto();
    dto.MyProperty = 666;

    var value = prop.GetValue(dto);
    // value == 666
}

class MyDto
{
    public int MyProperty { get; set; }
}

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

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

    var 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;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.