2009-02-03 23 views
13

मैं सोच रहा हूँ क्या यह इस काम की तरह कुछ बनाने के लिए ले जाएगा:सी # सुविधा का अनुरोध: गुमनाम प्रकार पर इंटरफेस को लागू

using System; 

class Program 
{ 
    static void Main() 
    { 
     var f = new IFoo { 
        Foo = "foo", 
        Print =() => Console.WriteLine(Foo) 
      }; 
    } 
} 

interface IFoo 
{ 
    String Foo { get; set; } 
    void Print(); 
} 

गुमनाम कुछ इस तरह दिखेगा बनाया प्रकार:

internal sealed class <>f__AnonymousType0<<Foo>j__TPar> : IFoo 
{ 
    readonly <Foo>j__TPar <Foo>i__Field; 

    public <>f__AnonymousType0(<Foo>j__TPar Foo) 
    { 
     this.<Foo>i__Field = Foo; 
    } 

    public <Foo>j__TPar Foo 
    { 
     get { return this.<Foo>i__Field; } 
    } 

    public void Print() 
    { 
     Console.WriteLine(this.Foo); 
    } 
} 

क्या कोई कारण है कि संकलक ऐसा कुछ करने में असमर्थ होगा? यहां तक ​​कि गैर-शून्य विधियों या विधियों के लिए जो पैरामीटर लेते हैं, संकलक इंटरफ़ेस घोषणा से प्रकारों का अनुमान लगाने में सक्षम होना चाहिए।

अस्वीकरण: मुझे पता है कि इस समय संभव नहीं है और यह केवल इस उदाहरण मैं इस के सैद्धांतिक पहलुओं के बारे में अधिक दिलचस्पी है में एक ठोस वर्ग बनाने के लिए और अधिक समझ बनाने होता है।

+0

यह विचार कल मेरे मन के लिए आया था और मैं एक वर्ग 'बेनामी ' बनाने के लिए और Reflection.Emit' का उपयोग 'इस तरह के एक वर्ग इंटरफ़ेस टी को लागू करने का निर्माण करने की कोशिश की लेकिन यह tooooo महंगा है! तब मैंने एसओ पर अपनी किस्मत की कोशिश की और यह सवाल पाया, क्या अब तक आपकी कोई प्रगति है? –

उत्तर

8

अतिभारित सदस्यों, indexers, और स्पष्ट इंटरफेस कार्यान्वयन के साथ कुछ मुद्दों वहाँ होगा।

हालांकि, आप शायद इस तरह से वाक्यविन्यास को परिभाषित कर सकते हैं जो आपको उन समस्याओं को हल करने की अनुमति देता है।

दिलचस्प बात यह है कि आप पुस्तकालय लिखकर सी # 3.0 के साथ जो चाहते हैं उसके करीब हो सकते हैं। असल में, आप यह कर सकते हैं:

Create<IFoo> 
(
    new 
    { 
     Foo = "foo", 
     Print = (Action)(() => Console.WriteLine(Foo)) 
    } 
); 

जो आप चाहते हैं उसके करीब है। प्राथमिक अंतर "नए" कीवर्ड के बजाय "बनाएं" और एक तथ्य है जिसे आपको एक प्रतिनिधि प्रकार निर्दिष्ट करने की आवश्यकता है।

की घोषणा "बनाएँ" इस प्रकार दिखाई देगा:

T Create<T> (object o) 
{ 
//... 
} 

यह तो Reflection.Emit का उपयोग क्रम में गतिशील रूप से एक अंतरफलक कार्यान्वयन उत्पन्न करने के लिए होगा।

हालांकि, इस वाक्यविन्यास में स्पष्ट इंटरफ़ेस कार्यान्वयन और अधिभारित सदस्यों के साथ समस्याएं हैं, जिन्हें आप संकलक को बदलने के बिना हल नहीं कर सके।

एक विकल्प अज्ञात प्रकार के बजाय संग्रह प्रारंभकर्ता का उपयोग करना होगा। यही कारण है कि इस प्रकार दिखाई देगा:

Create 
{ 
    new Members<IFoo> 
    { 
     {"Print", ((IFoo @this)=>Console.WriteLine(Foo))}, 
     {"Foo", "foo"} 
    } 
} 

है कि आप के लिए सक्षम होगा करने के लिए: स्ट्रिंग पैरामीटर के लिए "IEnumerable.Current" की तरह कुछ निर्दिष्ट द्वारा

  1. हैंडल स्पष्ट इंटरफेस कार्यान्वयन।
  2. सदस्यों को परिभाषित करें। जोड़ें ताकि आपको प्रारंभकर्ता में प्रतिनिधि प्रकार निर्दिष्ट करने की आवश्यकता न हो।

आप इस लागू करने के लिए कुछ कार्य करने की आवश्यकता होगी:

  1. लेखक सी # प्रकार के नाम के लिए एक छोटा सा पार्सर। इसके लिए केवल "।", "[]", "<>", आईडी, और आदिम प्रकार के नाम की आवश्यकता होती है, इसलिए आप शायद कुछ घंटों में ऐसा कर सकते हैं
  2. कैश लागू करें ताकि आप केवल एक वर्ग उत्पन्न कर सकें प्रत्येक अद्वितीय इंटरफ़ेस
  3. प्रतिबिंब लागू करें। कोड कोड जीन। यह शायद सबसे अधिक 2 दिनों में ले जाएगा।
+0

यह भावना में बहुत अधिक था सवाल का - धन्यवाद! –

-1

यह वर्तमान में संभव नहीं होगा।

इसके बीच क्या अंतर होगा और इसके बजाय IFoo को एक ठोस वर्ग बनाना होगा? ऐसा लगता है कि यह बेहतर विकल्प हो सकता है।

यह क्या होगा? एक नया कंपाइलर और चेक के टन यह सुनिश्चित करने के लिए कि उन्होंने अन्य सुविधाओं को तोड़ दिया नहीं है। निजी तौर पर, मुझे लगता है कि डेवलपर्स को केवल अपनी कक्षा का एक ठोस संस्करण बनाने की आवश्यकता होगी।

2

जब तक हम एक इंटरफ़ेस इच्छा सूची डाल रहे हैं, मैं वास्तव में संकलक को बताने में सक्षम होना चाहता हूं कि कक्षा कक्षा परिभाषा के बाहर एक इंटरफ़ेस लागू करती है- यहां तक ​​कि एक अलग असेंबली में भी।

उदाहरण के लिए, मान लीजिए कि मैं विभिन्न संग्रह प्रारूपों से फ़ाइलों को निकालने के लिए प्रोग्राम पर काम कर रहा हूं। मैं विभिन्न पुस्तकालयों से मौजूदा कार्यान्वयन में खींचने में सक्षम होना चाहता हूं — कहें, शार्पज़िपलिब और एक वाणिज्यिक पीजीपी कार्यान्वयन — और नई कक्षाओं के बिना एक ही कोड का उपयोग करके दोनों पुस्तकालयों का उपभोग करें। फिर मैं सामान्य बाधाओं में किसी भी स्रोत से स्रोतों का उपयोग कर सकता हूं, उदाहरण के लिए।

एक और उपयोग संकलक को बताएगा कि System.Xml.Serialization.XmlSerializerSystem.Runtime.Serialization.IFormatter इंटरफ़ेस लागू करता है (यह पहले से ही करता है, लेकिन संकलक इसे नहीं जानता है)।

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

+0

हुह? "यह पहले से ही करता है, लेकिन संकलक इसे नहीं जानता" 1. नहीं, यह नहीं करता है। 2. यह Serialize/deserialize लागू करता है, लेकिन तीन गुण बाइंडर, संदर्भ, सरोगेट चयनकर्ता नहीं है। –

+0

एह: आप इसे बहुत आसान में shoehorn सकता है। यह _should_ बनाया गया है ताकि आपके पास एक विधि हो जो IFormatter इंस्टेंस स्वीकार करे और बाइनरीफॉर्मेटर, साबैफोर्मेटर, मौजूदा XmlSerializer, या शिकायत के बिना अपने स्वयं के IFormatter कार्यान्वयन को पास कर सके। –

+0

क्या आप जानते हैं कि ऑक्सीजन [यह है] (http://prismwiki.codegear.com/en/Provide_Mixin-like_functionality)? मैंने एक ऐसी सुविधा भी वर्णित की है जिसे मैं देखना चाहता हूं [यहां] (http://codecrafter.blogspot.com/2010/10/roles-in-c.html)। –

4

एक अज्ञात प्रकार को केवल पढ़ने योग्य गुणों को छोड़कर कुछ भी करने के लिए नहीं किया जा सकता है।

C# Programming Guide (Anonymous Types) का हवाला देते हुए:

"बेनामी प्रकार वर्ग प्रकार है कि एक या अधिक सार्वजनिक केवल पढ़ने के गुण से मिलकर हैं इस तरह के तरीकों या घटनाओं के रूप में कोई अन्य प्रकार के वर्ग के सदस्यों की अनुमति दी जाती है।। एक अज्ञात प्रकार ऑब्जेक्ट को छोड़कर किसी इंटरफ़ेस या प्रकार पर नहीं डाला जा सकता है। "

+3

में कर सकता है हाँ, लेकिन मैं उस परिभाषा को संशोधित करने की उम्मीद कर रहा हूं :) –

0

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

मुझे आश्चर्य है कि यह अधिक गतिशील भाषा में या सी # 4.0 में गतिशील प्रकार और डीएलआर के साथ भी आसान होगा?

शायद आज सी # में आशय का कुछ lambdas साथ प्राप्त किया जा सकता है:

void Main() { 
    var foo = new Foo(); 
    foo.Bar = "bar"; 
    foo.Print =() => Console.WriteLine(foo.Bar); 
    foo.Print(); 
} 


class Foo : IFoo { 
    public String Bar { get; set; }  
    public Action Print {get;set;} 
} 
1

आप जावा में anonymous classes की तरह कुछ हो सकता था:

using System; 

class Program { 
    static void Main() { 
    var f = new IFoo() { 
     public String Foo { get { return "foo"; } } 
     public void Print() { Console.WriteLine(Foo); } 
    }; 
    } 
} 

interface IFoo { 
    String Foo { get; set; } 
    void Print(); 
} 
-1

मैं "नया IFoo() {के माध्यम से जावा Amonimous कक्षा में प्रयोग किया जाता है ...} "Sintax और यह व्यावहारिक और आसान है आप जल्दी एक सरल अंतरफलक को लागू करना है।

एक नमूना के रूप में यह एक विरासत सिर्फ एक बार इस्तेमाल किया पाने के बजाय वस्तु पर IDisposable इस तरह से लागू करने के लिए अच्छा होगा नया वर्ग इसे लागू करने।

6

यह # 4 ग ओपनसोर्स ढांचे impromptu interface नकली यह आंतरिक रूप से डीएलआर प्रॉक्सी का उपयोग कर बॉक्स से बाहर। प्रदर्शन अच्छा है, हालांकि के रूप में अच्छा नहीं के रूप में परिवर्तन करता है, तो आप ही अस्तित्व में प्रस्तावित कर सकते हैं।

आवश्यकता है, लेकिन
using ImpromptuInterface.Dynamic; 

...

var f = ImpromptuGet.Create<IFoo>(new{ 
       Foo = "foo", 
       Print = ReturnVoid.Arguments(() => Console.WriteLine(Foo)) 
      }); 
1

यह अच्छा नहीं होगा। इनलाइन अज्ञात वर्ग:

List<Student>.Distinct(new IEqualityComparer<Student>() 
{ 
    public override bool Equals(Student x, Student y) 
    { 
     return x.Id == y.Id; 
    } 

    public override int GetHashCode(Student obj) 
    { 
     return obj.Id.GetHashCode(); 
    } 
}) 
1

मैं इसे यहां डंप करने जा रहा हूं। मैंने इसे थोड़ी देर पहले लिखा था, लेकिन आईआईआरसी यह ठीक काम करता है।

एक सहायक समारोह एक MethodInfo लेने के लिए और एक मिलान Func या Action के Type वापस जाने के लिए सबसे पहले। दुर्भाग्यवश, आपको प्रत्येक पैरामीटर के लिए एक शाखा की आवश्यकता है, और मैं स्पष्ट रूप से तीन पर रुक गया।

static Type GenerateFuncOrAction(MethodInfo method) 
{ 
    var typeParams = method.GetParameters().Select(p => p.ParameterType).ToArray(); 
    if (method.ReturnType == typeof(void)) 
    { 
     if (typeParams.Length == 0) 
     { 
      return typeof(Action); 
     } 
     else if (typeParams.Length == 1) 
     { 
      return typeof(Action<>).MakeGenericType(typeParams); 
     } 
     else if (typeParams.Length == 2) 
     { 
      return typeof(Action<,>).MakeGenericType(typeParams); 
     } 
     else if (typeParams.Length == 3) 
     { 
      return typeof(Action<,,>).MakeGenericType(typeParams); 
     } 
     throw new ArgumentException("Only written up to 3 type parameters"); 
    } 
    else 
    { 
     if (typeParams.Length == 0) 
     { 
      return typeof(Func<>).MakeGenericType(typeParams.Concat(new[] { method.ReturnType }).ToArray()); 
     } 
     else if (typeParams.Length == 1) 
     { 
      return typeof(Func<,>).MakeGenericType(typeParams.Concat(new[] { method.ReturnType }).ToArray()); 
     } 
     else if (typeParams.Length == 2) 
     { 
      return typeof(Func<,,>).MakeGenericType(typeParams.Concat(new[] { method.ReturnType }).ToArray()); 
     } 
     else if (typeParams.Length == 3) 
     { 
      return typeof(Func<,,,>).MakeGenericType(typeParams.Concat(new[] { method.ReturnType }).ToArray()); 
     } 
     throw new ArgumentException("Only written up to 3 type parameters"); 
    } 
} 

और अब विधि है कि एक सामान्य पैरामीटर के रूप में एक अंतरफलक लेता है और एक Type कि इंटरफ़ेस को लागू करता है और एक निर्माता है रिटर्न प्रत्येक विधि/गेटर के लिए एक Func या Action लेने (Activator.CreateInstance के माध्यम से कॉल किया जाना चाहिए)/सेटर। हालांकि, उन्हें कन्स्ट्रक्टर में रखने के लिए आपको सही क्रम जानने की जरूरत है। वैकल्पिक रूप से (टिप्पणी-आउट कोड) यह एक डीएलएल उत्पन्न कर सकता है जिसे आप संदर्भित कर सकते हैं और सीधे प्रकार का उपयोग कर सकते हैं।

static Type GenerateInterfaceImplementation<TInterface>() 
{ 
    var interfaceType = typeof(TInterface); 
    var funcTypes = interfaceType.GetMethods().Select(GenerateFuncOrAction).ToArray(); 

    AssemblyName aName = 
     new AssemblyName("Dynamic" + interfaceType.Name + "WrapperAssembly"); 
    var assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
      aName, 
      AssemblyBuilderAccess.Run/*AndSave*/); // to get a DLL 

    var modBuilder = assBuilder.DefineDynamicModule(aName.Name/*, aName.Name + ".dll"*/); // to get a DLL 

    TypeBuilder typeBuilder = modBuilder.DefineType(
     "Dynamic" + interfaceType.Name + "Wrapper", 
      TypeAttributes.Public); 

    // Define a constructor taking the same parameters as this method. 
    var ctrBuilder = typeBuilder.DefineConstructor(
     MethodAttributes.Public | MethodAttributes.HideBySig | 
      MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, 
     CallingConventions.Standard, 
     funcTypes); 


    // Start building the constructor. 
    var ctrGenerator = ctrBuilder.GetILGenerator(); 
    ctrGenerator.Emit(OpCodes.Ldarg_0); 
    ctrGenerator.Emit(
     OpCodes.Call, 
     typeof(object).GetConstructor(Type.EmptyTypes)); 

    // For each interface method, we add a field to hold the supplied 
    // delegate, code to store it in the constructor, and an 
    // implementation that calls the delegate. 
    byte methodIndex = 0; 
    foreach (var interfaceMethod in interfaceType.GetMethods()) 
    { 
     ctrBuilder.DefineParameter(
      methodIndex + 1, 
      ParameterAttributes.None, 
      "del_" + interfaceMethod.Name); 

     var delegateField = typeBuilder.DefineField(
      "del_" + interfaceMethod.Name, 
      funcTypes[methodIndex], 
      FieldAttributes.Private); 

     ctrGenerator.Emit(OpCodes.Ldarg_0); 
     ctrGenerator.Emit(OpCodes.Ldarg_S, methodIndex + 1); 
     ctrGenerator.Emit(OpCodes.Stfld, delegateField); 

     var metBuilder = typeBuilder.DefineMethod(
      interfaceMethod.Name, 
      MethodAttributes.Public | MethodAttributes.Virtual | 
       MethodAttributes.Final | MethodAttributes.HideBySig | 
       MethodAttributes.NewSlot, 
      interfaceMethod.ReturnType, 
      interfaceMethod.GetParameters() 
       .Select(p => p.ParameterType).ToArray()); 

     var metGenerator = metBuilder.GetILGenerator(); 
     metGenerator.Emit(OpCodes.Ldarg_0); 
     metGenerator.Emit(OpCodes.Ldfld, delegateField); 

     // Generate code to load each parameter. 
     byte paramIndex = 1; 
     foreach (var param in interfaceMethod.GetParameters()) 
     { 
      metGenerator.Emit(OpCodes.Ldarg_S, paramIndex); 
      paramIndex++; 
     } 
     metGenerator.EmitCall(
      OpCodes.Callvirt, 
      funcTypes[methodIndex].GetMethod("Invoke"), 
      null); 

     metGenerator.Emit(OpCodes.Ret); 
     methodIndex++; 
    } 

    ctrGenerator.Emit(OpCodes.Ret); 

    // Add interface implementation and finish creating. 
    typeBuilder.AddInterfaceImplementation(interfaceType); 
    var wrapperType = typeBuilder.CreateType(); 
    //assBuilder.Save(aName.Name + ".dll"); // to get a DLL 

    return wrapperType; 
} 

आप इसका उपयोग उदाहरण के रूप में कर सकते हैं

public interface ITest 
{ 
    void M1(); 
    string M2(int m2, string n2); 
    string prop { get; set; } 

    event test BoopBooped; 
} 

Type it = GenerateInterfaceImplementation<ITest>(); 
ITest instance = (ITest)Activator.CreateInstance(it, 
    new Action(() => {Console.WriteLine("M1 called"); return;}), 
    new Func<int, string, string>((i, s) => "M2 gives " + s + i.ToString()), 
    new Func<String>(() => "prop value"), 
    new Action<string>(s => {Console.WriteLine("prop set to " + s);}), 
    new Action<test>(eh => {Console.WriteLine(eh("handler added"));}), 
    new Action<test>(eh => {Console.WriteLine(eh("handler removed"));})); 

// or with the generated DLL 
ITest instance = new DynamicITestWrapper(
    // parameters as before but you can see the signature 
    ); 
संबंधित मुद्दे