2012-06-12 16 views
10
साथ

यह यह करने के लिए संभव है एक विशेषता पैरामीटर पर पैरामीटर। क्या यह नीचे करना संभव है?निर्माता गुण लैम्ब्डा

class ExpandableQueryAttribute: Attribute { 
    private LambdaExpression someLambda; 
    //ctor 
    public ExpandableQueryMethodAttribute(LambdaExpression expression) 
    { 
     someLambda = expression 
    } 
} 

//usage: 
static LambdaExpression exp = 
     (Expression<Func<IQueryable<Person>, Person>>) 
     (p => p.FirstOrDefault()); 

[ExpandableQueryAttribute(exp)] //error here 
// "An attribute argument must be a constant expression, typeof expression 
// or array creation expression of an attribute parameter type" 

मेरा लक्ष्य एक विधि या विशेषता का निर्माता में लैम्ब्डा निर्दिष्ट करने के लिए है (भले ही मैं एक पूर्ण विधि नामित घोषित करने और विधि का नाम किसी भी तरह से पारित करने के लिए है, कि करने के लिए ठीक हो जाएगा) ।

  1. पैरामीटर प्रकार बदल सकते हैं, लेकिन यह महत्वपूर्ण है कि विशेषता निर्माता है कि पैरामीटर ले जा सकते हैं और किसी तरह से प्रकार LambdaExpression के एक क्षेत्र निर्दिष्ट करने के लिए

  2. मैं की घोषणा चाहते सक्षम हो लैम्बडा/विधि विशेषता कन्स्ट्रक्टर या इनलाइन पर कॉल के ठीक ऊपर होने के लिए, ताकि आपको यह देखने के लिए दूर जाना पड़े कि क्या हो रहा है।

तो इन विकल्पों ठीक होगा, लेकिन कोई किस्मत उन्हें काम करने के लिए हो रही है:

public static ... FuncName(...){...} 

[ExpandableQueryAttribute(FuncName)] 
// ... 

या

//lambdas aren't allowed inline for an attribute, as far as I know 
[ExpandableQueryAttribute(q => q.FirstOrDefault())] 
// ... 

मौजूदा काम के आसपास निर्माता के लिए एक नंबर आईडी पारित करने के लिए है ("तर्क निरंतर होना चाहिए" आवश्यकता को पूरा करना), जिसका उपयोग कन्स्ट्रक्टर द्वारा एक शब्दकोश में लुकअप करने के लिए किया जाता है जहां अभिव्यक्ति पहले जोड़ दी गई थी। यह सुधारने/सरल बनाने की उम्मीद कर रहा था, लेकिन मुझे एहसास है कि यह विशेषता रचनाकारों पर सीमाओं के कारण बेहतर नहीं होता है।

+0

अपनी भावना का पालन करें ... विशेषता तर्क पर सीमा स्पष्ट है। –

+1

इस लिंक पर सवाल भी पूछा गया था। जवाब यह था कि यह वर्तमान में संभव नहीं है। http://social.msdn.microsoft.com/Forums/en/vcsharp2008prerelease/thread/0d18c410-07b0-41cc-9c7f-9494633ca101 – Jamey

+0

@ जेमी यिप, यह आखिरी विकल्प है जो मैंने सूचीबद्ध किया था जो मुझे पता था कि एक सीमा थी। अभिव्यक्ति को एक चर के रूप में घोषित करके उस पर काम करने की उम्मीद थी, लेकिन फिर "निरंतर होना चाहिए" आवश्यकता मुझे मिली। हालांकि कामकाज दिलचस्प है, और मैं इसके बदलाव की कोशिश करने जा रहा हूं। – AaronLS

उत्तर

7

कैसे इस बारे में:

class ExpandableQueryAttribute : Attribute 
    { 

     private LambdaExpression someLambda; 
     //ctor 
     public ExpandableQueryAttribute(Type hostingType, string filterMethod) 
     { 
      someLambda = (LambdaExpression)hostingType.GetField(filterMethod).GetValue(null); 
      // could also use a static method 
     } 
    } 

इस आप एक क्षेत्र के लिए अपने लैम्ब्डा आवंटित और फिर रन टाइम पर उस में चूसना, हालांकि सामान्य रूप में मैं संकलन समय पर यह करने के लिए PostSharp की तरह कुछ का उपयोग करना चाहते देना चाहिए ।

सरल उपयोग का उदाहरण देखने

public class LambdaExpressionAttribute : Attribute 
    { 
     public LambdaExpression MyLambda { get; private set; } 
     //ctor 
     public LambdaExpressionAttribute(Type hostingType, string filterMethod) 
     { 
      MyLambda = (LambdaExpression)hostingType.GetField(filterMethod).GetValue(null); 
     } 
    } 

    public class User 
    { 
     public bool IsAdministrator { get; set; } 
    } 

    public static class securityExpresions 
    { 
     public static readonly LambdaExpression IsAdministrator = (Expression<Predicate<User>>)(x => x.IsAdministrator); 
     public static readonly LambdaExpression IsValid = (Expression<Predicate<User>>)(x => x != null); 

     public static void CheckAccess(User user) 
     { 
      // only for this POC... never do this in shipping code 
      System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); 
      var method = stackTrace.GetFrame(1).GetMethod(); 

      var filters = method.GetCustomAttributes(typeof(LambdaExpressionAttribute), true).OfType<LambdaExpressionAttribute>(); 
      foreach (var filter in filters) 
      { 
       if ((bool)filter.MyLambda.Compile().DynamicInvoke(user) == false) 
       { 
        throw new UnauthorizedAccessException("user does not have access to: " + method.Name); 
       } 
      } 

     } 
    } 

    public static class TheClass 
    { 
     [LambdaExpression(typeof(securityExpresions), "IsValid")] 
     public static void ReadSomething(User user, object theThing) 
     { 
      securityExpresions.CheckAccess(user); 
      Console.WriteLine("read something"); 
     } 

     [LambdaExpression(typeof(securityExpresions), "IsAdministrator")] 
     public static void WriteSomething(User user, object theThing) 
     { 
      securityExpresions.CheckAccess(user); 
      Console.WriteLine("wrote something"); 
     } 

    } 


    static void Main(string[] args) 
    { 

     User u = new User(); 
     try 
     { 
      TheClass.ReadSomething(u, new object()); 
      TheClass.WriteSomething(u, new object()); 
     } 
     catch(Exception e) 
     { 
      Console.WriteLine(e); 
     } 
    } 
+0

क्या आप जोड़ सकते हैं एक उपयोग उदाहरण? अप अनुमान में मतदान :) –

+0

@ChrisMcCall ने एक साधारण उदाहरण जोड़ा – Yaur

+0

ठीक है, अब मैं इसे प्राप्त करता हूं, लैम्ब्डा को कहीं और घोषित करता हूं और इसके नाम से इसका जिक्र करता हूं ... चालाक! –

3

यह संभव नहीं है क्योंकि आप एक विशेषता में क्या कर सकते हैं सीएलआर के बाइनरी डीएलएल प्रारूप में फिट होने की आवश्यकता है और मनमाने ढंग से ऑब्जेक्ट प्रारंभ करने के लिए कोई तरीका नहीं है। इसके लिए आप उदाहरण के लिए एक शून्य मूल्य नहीं पारित कर सकते हैं। प्रतिबंध बहुत सख्त हैं।

0

हालांकि आपके पास विशेषताओं के लिए जटिल कन्स्ट्रक्टर नहीं हो सकता है, कुछ स्थितियों में एक कार्य-परिसर उस विशेषता के लिए सार्वजनिक संपत्ति है और उसे रन-टाइम पर अपडेट करना है।

कक्षा के किसी ऑब्जेक्ट पर स्वयं बिंदु जो उसके गुणों पर कुछ विशेषताओं को शामिल करता है। LocalDisplayNameAttribute एक कस्टम विशेषता है।

निम्न कोड रन-टाइम पर मेरी कस्टम विशेषता वर्ग की संसाधनकी संपत्ति सेट करेगा। फिर उस बिंदु पर आप जो भी टेक्स्ट चाहते हैं उसे बाहर निकालने के लिए आप डिस्प्लेनाम को ओवरराइड कर सकते हैं।

 static public void UpdateAttributes(object self) 
    { 
     foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(self)) 
     { 
      LocalDisplayNameAttribute attr = 
       prop.Attributes[typeof(LocalDisplayNameAttribute)] 
        as LocalDisplayNameAttribute; 

      if (attr == null) 
      { 
       continue; 
      } 

      attr.ResourceKey = prop.Name; 
     } 
    } 
संबंधित मुद्दे