2013-01-03 15 views
5

मेरे पास एक कारखाना विधि है जो तीन enum मानों के आधार पर सही उप वर्ग देता है। ऐसा करने का एक तरीका है स्विच में स्विच में स्विच का उपयोग करना होगा। जाहिर है, मुझे वह विकल्प बहुत पसंद नहीं है।स्विच में स्विच को प्रतिस्थापित करने के लिए मैं सी # में कस्टम-विशेषताओं का उपयोग कैसे कर सकता हूं?

मैंने सोचा कि एक और विकल्प सी # में विशेषताओं का उपयोग करना होगा। प्रत्येक उप वर्ग में उस 3 enum मानों के साथ एक विशेषता होगी और कारखाने में मुझे केवल उस कक्षा को प्राप्त करना होगा जिसमें कारखाने में मौजूद एनम मूल्यों के अनुरूप समान enum मान हों।

हालांकि, मैं विशेषताओं के लिए काफी नया हूं और मुझे वेब में कोई उपयुक्त समाधान नहीं मिला। अगर कोई है, तो मुझे कुछ संकेत या कोड की कुछ पंक्तियां दे सकती हैं, मैं वास्तव में इसकी सराहना करता हूं!

+0

क्या आप 2 बार 2 स्थिति (2 एनम मूल्यों के साथ स्विच में स्विच) के साथ समस्या का वर्णन कर सकते हैं? –

+3

मुझे लगता है कि अगर आप सबक्लास पर विशेषताओं का उपयोग करते हैं, तो आपको इसका उपयोग करना होगा स्विच में स्विच में स्विच। मो फिर से आपको सभी उप-वर्गों के लिए विशेषता मान प्राप्त करने के लिए प्रतिबिंब का उपयोग करना होगा और जो धीमा होगा। – 24x7Programmer

उत्तर

3

सबसे पहले, अपनी विशेषता घोषित करने और अपनी कक्षाओं में जोड़ें।

enum MyEnum 
{ 
    Undefined, 
    Set, 
    Reset 
} 

class MyEnumAttribute : Attribute 
{ 
    public MyEnumAttribute(MyEnum value) 
    { 
     Value = value; 
    } 

    public MyEnum Value { get; private set; } 
} 

[MyEnum(MyEnum.Reset)] 
class ResetClass 
{ 
} 

[MyEnum(MyEnum.Set)] 
class SetClass 
{ 
} 

[MyEnum(MyEnum.Undefined)] 
class UndefinedClass 
{ 
} 

फिर, आप इस कोड का उपयोग अपने enums और प्रकारों के साथ एक शब्दकोश बनाने के लिए कर सकते हैं, और गतिशील रूप से एक प्रकार का निर्माण कर सकते हैं।

//Populate a dictionary with Reflection 
var dictionary = Assembly.GetExecutingAssembly().GetTypes(). 
    Select(t => new {t, Attribute = t.GetCustomAttribute(typeof (MyEnumAttribute))}). 
    Where(e => e.Attribute != null). 
    ToDictionary(e => (e.Attribute as MyEnumAttribute).Value, e => e.t); 
//Assume that you dynamically want an instance of ResetClass 
var wanted = MyEnum.Reset; 
var instance = Activator.CreateInstance(dictionary[wanted]); 
//The biggest downside is that instance will be of type object. 
//My solution in this case was making each of those classes implement 
//an interface or derive from a base class, so that their signatures 
//would remain the same, but their behaviors would differ. 

आप शायद नोटिस सकते हैं, Activator.CreateInstance बुला performant नहीं है। इसलिए, यदि आप प्रदर्शन को थोड़ा सुधारना चाहते हैं, तो आप शब्दकोश को Dictionary<MyEnum,Func<object>> में बदल सकते हैं और प्रकारों को मानों के रूप में जोड़ने के बजाय आप अपनी प्रत्येक कक्षा के निर्माता को लपेटकर और उन्हें ऑब्जेक्ट्स के रूप में वापस करने के कार्यों को जोड़ देंगे।

EDIT: मैं this पृष्ठ से अनुकूलित ConstructorFactory कक्षा जोड़ रहा हूं।

static class ConstructorFactory 
{ 
    static ObjectActivator<T> GetActivator<T>(ConstructorInfo ctor) 
    { 
     var paramsInfo = ctor.GetParameters(); 
     var param = Expression.Parameter(typeof(object[]), "args"); 
     var argsExp = new Expression[paramsInfo.Length]; 
     for (var i = 0; i < paramsInfo.Length; i++) 
     { 
      Expression index = Expression.Constant(i); 
      var paramType = paramsInfo[i].ParameterType; 
      Expression paramAccessorExp = Expression.ArrayIndex(param, index); 
      Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType); 
      argsExp[i] = paramCastExp; 
     } 
     var newExp = Expression.New(ctor, argsExp); 
     var lambda = Expression.Lambda(typeof(ObjectActivator<T>), newExp, param); 
     var compiled = (ObjectActivator<T>)lambda.Compile(); 
     return compiled; 
    } 

    public static Func<T> Create<T>(Type destType) 
    { 
     var ctor = destType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).First(); 
     Func<ConstructorInfo, object> activatorMethod = GetActivator<Type>; 
     var method = typeof(ConstructorFactory).GetMethod(activatorMethod.Method.Name, BindingFlags.Static | BindingFlags.NonPublic); 
     var generic = method.MakeGenericMethod(destType); 
     dynamic activator = generic.Invoke(null, new object[] { ctor }); 
     return() => activator(); 
    } 

    delegate T ObjectActivator<out T>(params object[] args); 
} 

आप Activator.CreateInstance के लिए एक विकल्प के रूप में उपयोग कर सकते हैं, यह अधिक से अधिक प्रदर्शन करता है, तो परिणाम कैश किया गया है प्रदान करता है।

var dictionary = Assembly.GetExecutingAssembly().GetTypes(). 
    Select(t => new { t, Attribute = t.GetCustomAttribute(typeof(MyEnumAttribute)) }). 
    Where(e => e.Attribute != null). 
    ToDictionary(e => (e.Attribute as MyEnumAttribute).Value, 
       e => ConstructorFactory.Create<object>(e.t)); 
var wanted = MyEnum.Reset; 
var instance = dictionary[wanted](); 
+0

मुझे कुंजी को तब तक एक्सेस करने के विचार को पसंद है जब कुंजी से एक्सेस किया जा सके। बहुत बढ़िया जवाब। –

2

इस आलेख को देखें: Creating Custom Attributes। गुणों और उनके मान प्राप्त करने के लिए आप प्रतिबिंब (उदाहरण के लिए GetCustomAttributes) का उपयोग कर सकते हैं।

आशा इस मदद करता है

1
[AttributeUsage(AttributeTargets.Class)] 
    public class SampleClass : Attribute { 
     public SampleClass() : base() { } 
     public SampleClass(YourEnum attributeValue) : this() { MyAttributeProperty = attributeValue; } 
     public YourEnum MyAttributeProperty { get; set; } 
    } 

    public enum YourEnum { Value1, Value2, Value3 } 

    [SampleClass(YourEnum.Value1)] 
    public class ExampleValue1Class { } 

    public class LoadOnlyClassesWithEnumValue1 { 
     public LoadOnlyClassesWithEnumValue1() { 

      Type[] allTypes = Assembly.GetExecutingAssembly().GetExportedTypes(); 
      foreach (var type in allTypes) { 
       if (type.GetCustomAttributes(typeof(SampleClass), false).Length > 0) { 
        SampleClass theAttribute = type.GetCustomAttributes(typeof(SampleClass), false).Single() as SampleClass; 
        // this type is using SampleClass - I use .Single() cause I don't expect multiple SampleClass attributes, change ths if you want 
        // specify true instead of false to get base class attributes as well - i.e. ExampleValue1Class inherits from something else which has a SampleClass attribute 
        switch (theAttribute.MyAttributeProperty) { 
         case YourEnum.Value1: 
          // Do whatever 
          break; 
         case YourEnum.Value2: 
          // you want 
          break; 
         case YourEnum.Value3: 
         default: 
          // in your switch here 
          // You'll find the ExampleValue1Class object should hit the Value1 switch 
          break; 
        } 
       } 
      } 
     } 
    } 

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

0

जानकारी रखने के लिए विशेषताओं का उपयोग करना संभव है, लेकिन आखिरकार निर्णय प्रक्रिया अभी भी होनी चाहिए और संभवतः बहुत अलग नहीं होगा; विशेषताओं की अतिरिक्त जटिलता के साथ। निर्णय की प्रकृति इस बात पर ध्यान दिए बिना कि मौजूदा तीन गणनाओं या विशेषताओं से निर्णय लेने के लिए आपको जानकारी कहां मिलती है।

यह तीन गणनाओं को गठबंधन करने के तरीके को देखने के लिए और अधिक उपयोगी साबित हो सकता है।

Enums कोई अभिन्न प्रकार हो सकता है, इसलिए नेस्टेड (और अनावश्यक) स्विच को खत्म करने का सबसे आसान तरीका एक साथ जोड़ों को गठबंधन करना है। यह गणना सबसे आसान है अगर गणना ध्वज मूल्यों का संग्रह है। यही है, गणना के प्रत्येक मूल्य में बाइनरी स्ट्रिंग में एक बिट का मान होता है (पहली बिट के लिए 1, दूसरी बिट के लिए 2, तीसरे के लिए 4, 8, 16 और इसी तरह)।

बशर्ते प्रत्येक गणना के मूल्यों को एक साथ जोड़ा जा सकता है, यह चयन प्रक्रिया को एक स्विच स्टेटमेंट में कम कर देता है। यह गणना मूल्यों को संयोजित करने, गुणा करने या जोड़ने के द्वारा किया जा सकता है - लेकिन वे एक साथ कैसे जुड़ते हैं, गणनाओं पर निर्भर करता है, और अधिक जानकारी के बिना अधिक निश्चित दिशा प्रदान करना मुश्किल है।

1

एक और समाधान निर्भरता इंजेक्शन (डीआई) कंटेनर का उपयोग करना होगा। Unity डि आप कर सकते हैं का उपयोग कर उदाहरण के लिए: के प्रयोग पर

// Register a named type mapping 
myContainer.RegisterType<IMyObject, MyRealObject1>(MyEnum.Value1.ToString()); 
myContainer.RegisterType<IMyObject, MyRealObject2>(MyEnum.Value2.ToString()); 
myContainer.RegisterType<IMyObject, MyRealObject3>(MyEnum.Value3.ToString()); 
// Following code will return a new instance of MyRealObject1 
var mySubclass = myContainer.Resolve<IMyObject>(myEnum.Value1.ToString()); 

उदाहरण एकता: Implementing the Microsoft Unity (Dependency Injection) Design Pattern

बेशक आप किसी भी डि कंटेनर (कैसल विंडसर, StructureMap, Ninject का उपयोग कर सकते का

यहाँ उपलब्ध कुछ एक सूची है। .NET DI कंटेनर List of .NET Dependency Injection Containers (IOC)

संबंधित मुद्दे