लैम्ब्डा अभिव्यक्ति से संपत्ति का नाम वापस लेना


512

लैंबडा एक्सप्रेशन के माध्यम से पास होने पर संपत्ति का नाम प्राप्त करने का एक बेहतर तरीका है? यहाँ मेरे पास वर्तमान में क्या है।

जैसे।

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

इसे केवल एक झिल्ली के रूप में कास्टिंग करके काम किया जब संपत्ति एक स्ट्रिंग थी। क्योंकि सभी गुण तार नहीं हैं, मुझे ऑब्जेक्ट का उपयोग करना था लेकिन फिर यह उन लोगों के लिए एक unaryexpression लौटाएगा।

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

अच्छे कोड के रूप में बेहतर है? मुझे ऐसा नहीं लगता। टाइपकास्टिंग केवल समग्र अभिव्यक्ति तक फैली हुई है, इसलिए आपको रनटाइम के दौरान वास्तव में चेक की आवश्यकता होती है। :(
माइकलजी

हाँ ... बस सोच रहा था कि क्या यह करने का एक बेहतर तरीका है, क्योंकि यह मुझे थोड़ा हैकरी लगा। लेकिन अगर यह शांत है। धन्यवाद।
शोटाइम

मैंने आपकी टिप्पणी को फिर से अपडेट किया; लेकिन एक स्ट्रिंग प्राप्त करने के लिए एक लैम्ब्डा का उपयोग करना ताकि आप डायनामिक LINQ का उपयोग कर सकें, मुझे पीछे की चीजों के रूप में कर सकता है ... यदि आप लैम्बडा का उपयोग करते हैं, तो लैम्बडा का उपयोग करें; - आपको एक चरण में पूरी क्वेरी करने की आवश्यकता नहीं है - आप इस्तेमाल कर सकते हैं "नियमित / लैम्ब्डा" OrderBy, "गतिशील LINQ / स्ट्रिंग" कहाँ, आदि
मार्क Gravell


4
सभी के लिए एक नोट: सदस्य MemberExpressionके नाम को प्राप्त करने के लिए केवल यहां सूचीबद्ध दृष्टिकोण का उपयोग करें , वास्तविक स्वयं प्राप्त करने के लिए नहींMemberInfo , क्योंकि MemberInfoलौटाए जाने की गारंटी कुछ "विकृत: आधार" परिदृश्यों में परिलक्षित प्रकार की नहीं है। देखें लैम्ब्डा-एक्सप्रेशन-नॉट-रिटर्निंग-अपेक्षित-सदस्यिनफो । मुझे एक बार फँसाया। स्वीकृत उत्तर भी इससे ग्रस्त है।
नवाफाल

जवाबों:


350

मैंने हाल ही में एक बहुत ही समान काम किया है एक प्रकार का सुरक्षित ऑनप्रोपर्टीचेंज विधि।

यहाँ एक तरीका है कि मैं अभिव्यक्ति के लिए 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);

6
TSource के बारे में अंतिम जाँच क्यों है? लैम्ब्डा दृढ़ता से टाइप किया गया है इसलिए मुझे नहीं लगता कि यह आवश्यक है।
HappyNomad

16
इसके अलावा, 2012 के अनुसार, स्रोत पैरामीटर के बिना प्रकार का अनुमान ठीक काम करता है।
HappyNomad

4
@HappyNomad एक वस्तु की कल्पना करें जो एक सदस्य के रूप में है, तीसरे प्रकार का एक उदाहरण है। u => u.OtherType.OtherTypesPropertyइस तरह के एक मामले को बनाएगा जो अंतिम विवरण के लिए जाँच कर रहा है।
15:15 पर joshperry

5
अंतिम यदि कथन होना चाहिए: if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType) && !propInfo.ReflectedType.IsAssignableFrom(type))इंटरफेस के लिए भी अनुमति देने के लिए।
ग्राहम राजा

8
@GrayKing बस के रूप में ही नहीं होगा if(!propInfo.ReflectedType.IsAssignableFrom(type))?
कोनेल

192

मुझे एक और तरीका मिला जो आप कर सकते हैं वह था स्रोत और संपत्ति को दृढ़ता से टाइप करना और स्पष्ट रूप से लंबो के लिए इनपुट का अनुमान लगाना। यकीन नहीं है कि अगर सही शब्दावली है, लेकिन यहाँ परिणाम है।

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

और आवाज यह काम करता है।
सबको शुक्रीया।


4
यह समाधान थोड़ा अद्यतन होना चाहिए। कृपया निम्नलिखित लेख देखें - यहां एक लिंक है
पावेल सेरमक

1
इसका केवल एक विकल्प यदि आप ASP.Net MVC और केवल UI परत (HtmlHelper) के लिए करते हैं।
मार्क

3
c # 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
हाल ही में यह ( दूसरे प्रश्न से ) की कोशिश की , पता चला कि यह उपप्रकारों को नहीं संभालता है: o => o.Thing1.Thing2वापस आ जाएगा Thing2, नहीं Thing1.Thing2, जो गलत है यदि आप इसे EntityFramework में उपयोग करने की कोशिश कर रहे हैं, तो शामिल हैं
drzaus

1
AKA (field.Body UnaryExpression है? ((UnaryExpression) field.Body) .Operand: field.Body) में सदस्य के रूप में

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यदि आपकी अभिव्यक्ति एक संदर्भ प्रकार का प्रतिनिधित्व करती है। सब कुछ एक वस्तु पर डाला जा सकता है, लेकिन मूल्य प्रकारों को बॉक्सिंग करना होगा। यही कारण है कि UnaryExpression मौजूद है। संदर्भ।

पठनीयता (@ जॉयेन) के लिए, यहाँ एक विस्तृत समतुल्य है:

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

@flem, I पठनीयता के लिए <TField> को छोड़ देता है, क्या कोई समस्या है। लैम्ब्डाएक्सप्रेस.गेटनेम <बास्केट> (m => m.Quantity)
सोरेन

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

यह फ़ील्ड / प्रॉपर्टीज / इंडेक्सर्स / मेथड्स / एक्सटेंशन मेथड्स / स्ट्रक्चर्स ऑफ स्ट्रक्चर / क्लास / इंटरफेस / डेलीगेट / ऐरे को प्राप्त करने के लिए एक सामान्य कार्यान्वयन है। मैंने स्टैटिक / इंस्टेंस और नॉन-जेनेरिक / जेनरिक वेरिएंट के कॉम्बिनेशन के साथ टेस्ट किया है।

//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. यह constएस, एरे इंडेक्सर्स या 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 ));

यदि "someArray" कंसोल के लिए प्रिंट 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 नेस्टेड / सबप्रॉपर्टी को कैप्चर नहीं करते हैं।

पूर्व) के बजाय o => o.Thing1.Thing2रिटर्नThing1Thing1.Thing2

यदि आप EntityFramework के साथ काम करने की कोशिश कर रहे हैं तो यह अंतर महत्वपूर्ण है 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

(सीमांकक के लिए जाँच भी overkill हो सकता है)

डेमो (LinqPad)

प्रदर्शन + तुलना कोड - https://gist.github.com/zaus/6992590


1
+ 1 बहुत दिलचस्प। क्या आपने अपने कोड में इस विधि का उपयोग जारी रखा है? क्या यह ठीक है? क्या आपने कोई एज केस खोजा है?
बेंजामिन गेल

मैं आपके विचार को देखने में विफल हूं। आपके द्वारा लिंक किए गए उत्तर से o => o.Thing1.Thing2जाने Thing1पर आप जैसा कहते हैं, वैसा वापस नहीं आता Thing2। वास्तव में आपका उत्तर कुछ ऐसा देता है Thing1.Thing2जो वांछित हो सकता है या नहीं।
नवफाल

मामले के साथ काम नहीं करता है korman चेतावनियाँ: stackoverflow.com/a/11006147/661933 । हमेशा हैक से बचने के लिए बेहतर है।
नवफल

@nawfal # 1 - मूल समस्या यह है कि आप चाहते हैं Thing1.Thing2 , कभी नहीं Thing1। मैंने कहा Thing2, जिसका अर्थ मूल्य की o.Thing1.Thing2है, जो विधेय के बिंदु है। मैं उस इरादे को दर्शाने के लिए उत्तर को अपडेट करूंगा।
drzaus

@drzaus क्षमा करें मैं अभी भी आपको नहीं मिल रहा हूं। वास्तव में समझने की कोशिश कर रहा है। आप यह क्यों कहेंगे कि अन्य उत्तर यहां लौटते हैं Thing1? मुझे नहीं लगता कि यह पीछे हटता है।
नवफाल

6

मैं पूर्व C # 6 परियोजनाओं के लिए एक एक्सटेंशन विधि का उपयोग कर रहा हूं और C # 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"फू", "बार", या एक अपवाद लौटाया जाना चाहिए - यानी क्या आपको इसकी पुनरावृति की आवश्यकता है।

(पुनः टिप्पणी) अधिक लचीली छँटाई के लिए, यहाँ देखें ।


हाँ ... इसकी केवल पहली स्तर की चीज़ है, जिसका उपयोग छँटाई कॉलम लिंक बनाने के लिए किया जाता है। जैसे। यदि मेरे पास एक मॉडल है और मैं कॉलम नाम को प्रदर्शित करना चाहता हूं, तो मैं संपत्ति के नाम को प्राप्त करने के लिए ऑब्जेक्ट के लिए एक दृढ़ता से टाइप किए गए लिंक का उपयोग कर सकता हूं, जिसके लिए डायनेमिक लाइनक में गाय नहीं होगी। खुश होती है।
शोटाइम

ToStringएकात्मक भावों के लिए कुरूप परिणाम देना चाहिए।
नवाफल

3

मैंने ObjectStateEntry पर एक एक्सटेंशन मेथड बनाया, जो कि एक प्रकार के सुरक्षित तरीके से संशोधित करने के लिए संपत्तियों (एंटिटी फ्रेमवर्क POCO क्लासेस के) को सक्षम करने के लिए है, क्योंकि डिफ़ॉल्ट विधि केवल एक स्ट्रिंग को स्वीकार करती है। यहाँ संपत्ति से नाम प्राप्त करने का मेरा तरीका है:

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;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.