2017-07-11 13 views
6

है, मेरे पास निम्न सी # कोड है जो मुझे पसंद नहीं है।जेनेरिक टाइप पैरामीटर जो कि आईनेमेरेबल <T>

आवश्यकता कुछ भी है कि किसी भी IEnumerable<T> लागू करता है का उपयोग करता है दूसरी विधि है कि "2" प्रिंट है, लेकिन कुछ और पहली विधि है कि "1" प्रिंट उपयोग करता है।

एक बेवकूफ प्रदर्शन नीचे है। ICollection<int>, IList<int>, List<int> और int[] सभी IEnumerable<T> लागू लेकिन "1" बजाय "2"

using System; 
using System.Collections.Generic; 
using System.Linq.Expressions; 

namespace Test 
{ 
    public class Program 
    { 
     public static void Main() 
     { 
      var parent = new Parent<Class>(); 

      // OK: TProperty == int. Prints "1" 
      parent.Map(c => c.IntValue); 

      // OK: TProperty == int. Prints "2" 
      parent.Map(c => c.IEnumerableIntValue); 

      // Wrong: TProperty == ICollection<int>. Prints "1" 
      parent.Map(c => c.ICollectionIntValue); 

      // Wrong: TProperty == List<int>. Prints "1" 
      parent.Map(c => c.ListIntValue); 

      // Wrong: TProperty == int[]. Prints "1" 
      parent.Map(c => c.ArrayIntValue); 
     } 

     public class Class 
     { 
      public int IntValue { get; set; } 
      public IEnumerable<int> IEnumerableIntValue { get; set; } 
      public ICollection<int> ICollectionIntValue { get; set; } 
      public List<int> ListIntValue { get; set; } 
      public int[] ArrayIntValue { get; set; } 
     } 
    } 

    public class Parent<T> 
    { 
     public void Map<TProperty>(Expression<Func<T, TProperty>> expression) 
     { 
      Console.WriteLine("1"); 
     } 

     public void Map<TProperty>(Expression<Func<T, IEnumerable<TProperty>>> expression) 
     { 
      Console.WriteLine("2"); 
     } 
    } 
} 

मैं

public void Map<TEnumerable, TElement>(Expression<Func<T, TEnumerable>> expression) where TEnumerable : IEnumerable<TElement> 
{ 
    Console.WriteLine("2"); 
} 

को परिभाषा को बदलने की कोशिश की है छपा है, लेकिन यह स्पष्ट प्रकार पैरामीटर का उपयोग करने, जो की आवश्यकता है अस्वीकार्य:

parent.Map<int[], int>(c => c.ArrayIntValue); 

क्या किसी को संकलन समय पर सी # में इसे प्राप्त करने के बारे में कोई विचार है? किसी भी विचार की सराहना की जाती है। हो सकता है कि कॉन्ट्रैक्ट/कॉन्वर्सेंट प्रतिनिधि काम कर सकें? मैंने सी # कंपाइलर के साथ झगड़ा करने की कोशिश की है लेकिन कहीं भी नहीं मिला है।

+3

बीटीडब्ल्यू: स्ट्रिंग लागू करता है IENumerable । आपकी उम्मीदों का उपयोग करके, सभी कॉलों को "2" प्रिंट करना चाहिए। –

+0

अब यह एक दिलचस्प बात है कि मैंने @rind –

+2

के बारे में सोचा नहीं है INumerable के लिए अलग विधि बनाने के बारे में: 'सार्वजनिक शून्य मानचित्र संख्यात्मक (अभिव्यक्ति >> अभिव्यक्ति)' –

उत्तर

1

अद्यतन मेरे पिछले जवाब पूरी तरह गलत था, ठीक से के माध्यम से यह नहीं सोचा था।

नहीं, आप इसे इस तरह से नहीं कर सकते हैं। इसका कारण यह है कि TIEnumerable<T> से IEnumerable<T> के रूप में स्थिर रूप से टाइप की गई किसी भी चीज़ के लिए हमेशा बेहतर मिलान नहीं होगा, यह केवल जेनेरिक काम करता है; T से बेहतर जेनेरिक मैच नहीं हो सकता है जब तक कि आपके पास सटीक मिलान नहीं है।

void Foo<T>(T t) { } 
void Foo<T>(IEquatable<T> equatable) { } 

आप वास्तव में Foo(1) दूसरा अधिभार को हल करने की उम्मीद करेंगे:

निम्नलिखित पर विचार करें?

या Foo("hello") संकल्प Foo<char>(IEnumerable<char>) को जब लागू उम्मीदवार हैं है:

void Foo<T>(T t) { } 
void Foo<T>(IEnumerable<T> enumerable) { } 

सरल समाधान के लिए कोई स्पष्ट डाली बनाने के लिए है जब मानचित्रण:

parent.Map(c => c.ICollectionIntValue.AsEnumerable()); 
parent.Map(c => c.ListIntValue.AsEnumerable()); 
//etc. 

आप सकता है कल्पना मिश्रण कुछ करना निम्नलिखित पंक्तियों के साथ dynamic के साथ कुछ प्रतिबिंब ऊपर:

public void Map<TProperty>(Expression<Func<T, TProperty>> expression) 
{ 
    var genericInterfaces = typeof(TProperty).GetInterfaces().Where(i => i.IsGenericType); 
    var iEnumerables = genericInterfaces.Where(i => i.GetGenericTypeDefinition().Equals(typeof(IEnumerable<>))).ToList(); 

    if (iEnumerables.Count > 1) 
     throw new InvalidOperationException("Ambiguous IEnumerable<>"); 

    var iEnumerable = iEnumerables.FirstOrDefault(); 

    if (iEnumerable == null) 
    { 
     Console.WriteLine("1"); 
    } 
    else 
    { 
     //ok, we know we have an IEnumerable of something. Let the runtime figure it out. 
     Expression<Func<T, IEnumerable<dynamic>>> newExpression = e => expression.Compile()(e) as IEnumerable<dynamic>; 
     Map(newExpression); 
    } 
} 

public void Map<TProperty>(Expression<Func<T, IEnumerable<TProperty>>> expression) 
{ 
    Console.WriteLine("2"); 
} 
2

यह वास्तव में है कि आश्चर्य की बात है कि केवल विधि जिसका प्रकार तर्क स्पष्ट रूप से संकलक द्वारा निर्धारित किया जाता IEnumerable<T> होने के लिए एक है कि वास्तव में IEnumerable<T> स्पष्ट रूप से संबंधित है है?

यहाँ एक unoptimised कार्यान्वयन जो गतिशील रूप से पता लगाएँ कि क्या प्रकार TProperty स्पष्ट रूप से एक को लागू करता है (और केवल एक) IEnumerable<> इंटरफेस के संस्करण बंद कर दिया, आप उस विशेष मामले में अलग ढंग से अभिव्यक्ति पेड़ पर कार्रवाई करने के लिए अनुमति देता है काम करता है।

using System; 
using System.Collections.Generic; 
using System.Linq.Expressions; 

namespace Test 
{ 
    public class Program 
    { 
     public static void Main() 
     { 
      var parent = new Parent<Class>(); 

      // OK: TProperty == int. Prints "1" 
      parent.Map(c => c.IntValue); 

      // OK: TProperty == int. Prints "2" 
      parent.Map(c => c.IEnumerableIntValue); 

      // Wrong: TProperty == ICollection<int>. Prints "1" 
      parent.Map(c => c.ICollectionIntValue); 

      // Wrong: TProperty == List<int>. Prints "1" 
      parent.Map(c => c.ListIntValue); 

      // Wrong: TProperty == int[]. Prints "1" 
      parent.Map(c => c.ArrayIntValue); 
     } 

     public class Class 
     { 
      public int IntValue { get; set; } 
      public IEnumerable<int> IEnumerableIntValue { get; set; } 
      public ICollection<int> ICollectionIntValue { get; set; } 
      public List<int> ListIntValue { get; set; } 
      public int[] ArrayIntValue { get; set; } 
     } 
    } 

    public class Parent<T> 
    { 
     public void Map<TProperty>(Expression<Func<T, TProperty>> expression) 
     { 
      if (ReflectionHelpers.IsUnambiguousIEnumerableOfT(typeof(TProperty))) 
      { 
       MapMany(expression); 
      } 
      else 
      { 
       MapOne(expression); 
      } 
     } 

     void MapOne(Expression expression) 
     { 
      Console.WriteLine("1"); 
     } 

     void MapMany(Expression expression) 
     { 
      Console.WriteLine("2"); 
     } 
    } 

    static class ReflectionHelpers 
    { 
     public static bool IsUnambiguousIEnumerableOfT(Type type) 
     { 
      // Simple case - the type *is* IEnumerable<T>. 
      if (IsIEnumerableOfT(type)) { 
       return true; 
      } 

      // Harder - the type *implements* IEnumerable<T>. 
      HashSet<Type> distinctIEnumerableImplementations = new HashSet<Type>(); 

      ExtractAllIEnumerableImplementations(type, distinctIEnumerableImplementations); 

      switch (distinctIEnumerableImplementations.Count) 
      { 
       case 0: return false; 
       case 1: return true; 

       default: 
        // This may or may not be appropriate for your purposes. 
        throw new NotSupportedException("Multiple IEnumerable<> implementations detected."); 
      } 
     } 

     private static bool IsIEnumerableOfT(Type type) 
     { 
      return type.IsGenericType 
       && type.GetGenericTypeDefinition() == typeof(IEnumerable<>); 
     } 

     private static void ExtractAllIEnumerableImplementations(Type type, HashSet<Type> implementations) 
     { 
      foreach (Type interfaceType in type.GetInterfaces()) 
      { 
       if (IsIEnumerableOfT(interfaceType)) { 
        implementations.Add(interfaceType); 
       } 

       ExtractAllIEnumerableImplementations(interfaceType, implementations); 
      } 
     } 
    } 
} 
+0

मुझे समस्या बिल्कुल नहीं दिखाई दे रही है। टाइप अनुमान केवल विफल रहता है और आपको जेनेरिक प्रकार पैरामीटर को स्पष्ट रूप से निर्दिष्ट करना होगा। – InBetween

+0

@InBetween, अच्छा बिंदु। यह बिल्कुल एक मजबूत तर्क नहीं था, इसलिए मैंने इसे हटाने और परिदृश्य-विशिष्ट कार्यवाही पर ध्यान केंद्रित करने के लिए चुना है। –

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