आइए मान लें कि मेरे पास निम्नानुसार परिभाषित एक सामान्य वर्ग है:

public class MyThing<T> {
     private static int NumberOfCallsToFoo;
     public void Foo(T item) {
          NumberOfCallsToFoo++;
          ...
     }
}

बाद में मैं किसी भी बंद प्रकार के MyThing सभी संभावित प्रकार के मापदंडों को जाने बिना कॉल की सभी संख्या एकत्र करना चाहता हूं ताकि मैं एक सूची प्रिंट कर सकूं जैसे:

  • MyThing<int>.Foo() पर कॉल की संख्या: 142
  • MyThing<Bar>.Foo() पर कॉल की संख्या: 39
  • आदि।

तो मुझे जो चाहिए वह गतिशील रूप से रन-टाइम से सभी निर्मित बंद प्रकार प्राप्त करने का एक तरीका है। कुछ इस तरह:

public IEnumerable<Type> GetClosedMyThingTypes() {
    // what to put here?
}

तब मैं NumberOfCallsToFoo फ़ील्ड प्राप्त करने के लिए प्रतिबिंब का उपयोग कर सकता था। इसका उद्देश्य एक विशाल प्रणाली का पता लगाना है जिसमें कई प्रकार और असेंबली शामिल हैं।

अपडेट करें: मुझे जो चाहिए उसे सुधारने की कोशिश करता हूं। एक कार्यक्रम के निष्पादन के समय में एक मनमाना क्षण में, मैं एक निश्चित सामान्य प्रकार के सभी (अब तक निर्मित) बंद प्रकार (उदाहरण नहीं) एकत्र करना चाहता हूं।

1
Dejan 9 सितंबर 2017, 03:57

3 जवाब

सबसे बढ़िया उत्तर

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

दुर्भाग्य से, इसे स्वयं करने के लिए आपको हर उस सामान्य प्रकार के उपकरण की आवश्यकता होगी जिसे आप ट्रैक करना चाहते हैं। यदि आपके पास सिर्फ एक है, तो यह ठीक हो सकता है। लेकिन हर सामान्य प्रकार के लिए ऐसा करना थोड़ा थकाऊ हो सकता है। उस स्थिति में, मैं एक एओपी उपकरण (जैसे पोस्टशर्प) देखना पसंद कर सकता हूं जो आपको इस उपकरण को अधिक स्वचालित तरीके से जोड़ने की अनुमति देगा।

उस ने कहा, आपकी समस्या का विवरण दिया गया है, मैं इसे कुछ इस तरह से देख सकता हूं:

class C
{
    protected static List<Type> _types = new List<Type>();

    public static void ReportCounts()
    {
        foreach (Type type in _types)
        {
            FieldInfo fi = type.GetField("_count", BindingFlags.Static | BindingFlags.NonPublic);

            Console.WriteLine($"{type.Name}: {fi.GetValue(null)}");
        }
    }
}

class C<T> : C
{
    static int _count;

    static C()
    {
        _types.Add(typeof(C<T>));
    }

    public void M()
    {
        _count++;
    }
}

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

डेमो कार्यक्रम:

class Program
{
    static void Main(string[] args)
    {
        C<int> c1 = new C<int>();
        C<bool> c2 = new C<bool>();

        c1.M();
        c2.M();
        c2.M();

        C.ReportCounts();
    }
}

उपज:

C`1: 1
C`1: 2

ध्यान दें कि सामान्य संपत्ति के नाम में प्रकार पैरामीटर शामिल नहीं है; यह सिर्फ घोषित सामान्य प्रकार का नाम है। यदि आप अधिक मित्रवत नाम चाहते हैं, तो आप यहां देख सकते हैं: C# सामान्य प्रकार का नाम प्राप्त करें

ध्यान दें कि ReportCounts() विधि में प्रतिबिंब संभावित रूप से महंगा है। सटीक उपयोग के आधार पर, जैसे कि कितनी बार गिनने का तरीका कहा जाता है बनाम रिपोर्ट कितनी बार बनाई जाती है, आप काउंट्स को Dictionary<Type, int> में संग्रहीत करके या पास किए गए या इसके साथ बनाए गए प्रतिनिधि को याद करके चीजों में सुधार कर सकते हैं। Expression प्रत्येक प्रकार के लिए फ़ील्ड को एक्सेस करने के लिए।

मेरा अनुमान है कि आप ReportCounts() को बहुत बार कॉल नहीं करते हैं और इसलिए प्रत्यक्ष प्रतिबिंब दृष्टिकोण ठीक है।

उपरोक्त निश्चित रूप से केवल एक गिनती को संभालता है। यदि आप कई विधियों के साथ काम कर रहे हैं, तो हो सकता है कि आप एक से अधिक फ़ील्ड चाहते हों, या एक एकल फ़ील्ड जो Dictionary<string, int> हो, जहाँ कुंजी विधि का नाम हो। आप इसे प्रति-प्रकार के अनुकूलन के साथ जोड़ सकते हैं और इसके बजाय बेस क्लास में Dictionary<Type, Dictionary<string, int>> के साथ वाइंड अप कर सकते हैं।

यहां एक विविधता है जिसमें इन अन्य सुविधाओं के कुछ उदाहरण शामिल हैं:

  • कई तरीकों को संभालता है
  • सामान्य प्रकार को एक्सेसर प्रतिनिधि पास करके प्रतिबिंब से बचा जाता है
  • प्रकार के नामों के लिए एक सरल "दोस्ताना नाम" विस्तार विधि शामिल है
class C
{
    protected static List<Func<(Type, Dictionary<string, int>)>>
        _countFieldAccessors = new List<Func<(Type, Dictionary<string, int>)>>();

    protected static void _AddType(Func<(Type, Dictionary<string, int>)> fieldAccess)
    {
        _countFieldAccessors.Add(fieldAccess);
    }

    public static void ReportCounts()
    {
        foreach (Func<(Type, Dictionary<string, int>)> fieldAccess in _countFieldAccessors)
        {
            var (type, counts) = fieldAccess();

            foreach (var kvp in counts)
            {
                Console.WriteLine($"{type.GetFriendlyName()}.{kvp.Key}: {kvp.Value}");
            }
        }
    }
}

class C<T> : C
{
    static Dictionary<string, int> _counts = new Dictionary<string, int>();

    static void _Increment(string name)
    {
        int count;

        _counts.TryGetValue(name, out count);
        _counts[name] = count + 1;
    }

    static C()
    {
        _AddType(() => (typeof(C<T>), _counts));
    }

    public void M1()
    {
        _Increment(nameof(M1));
    }

    public void M2()
    {
        _Increment(nameof(M2));
    }
}

static class Extensions
{
    public static string GetFriendlyName(this Type type)
    {
        return type.IsGenericType ?
            $"{type.Name.Substring(0, type.Name.IndexOf('`'))}<{string.Join(",", type.GetGenericArguments().Select(t => t.Name))}>" :
            type.Name;
    }
}

डेमो कार्यक्रम:

class Program
{
    static void Main(string[] args)
    {
        C<int> c1 = new C<int>();
        C<bool> c2 = new C<bool>();

        c1.M1();
        c1.M2();
        c1.M2();
        c2.M1();
        c2.M1();
        c2.M1();
        c2.M2();
        c2.M2();
        c2.M2();
        c2.M2();

        C.ReportCounts();
    }
}

उपज:

C.M1: 1
C.M2: 2
C.M1: 3
C.M2: 4
1
Peter Duniho 9 सितंबर 2017, 06:15

आप इसे रन टाइम पर कर सकते हैं, लेकिन आपको अपना कोड संशोधित करना होगा।

सबसे पहले, स्ट्रिंग्स की एक स्थिर, वैश्विक सूची घोषित करें, जिसमें बंद प्रकारों का पूरा नाम शामिल है, साथ ही इसे जोड़ने के लिए एक विधि भी है:

class Program
{
    static public List<string> _closedTypes = new List<string>();

    static public void RegisterClosedType(Type t)
    {
        _closedTypes.Add(String.Format("{0}<{1}>",
            t.Name.Split('`')[0],
            String.Join(",", t.GenericTypeArguments.Select(q => q.Name))
            ));
    }
}

फिर अपने प्रत्येक सामान्य प्रकार में एक स्थिर कंस्ट्रक्टर जोड़ें। हम इस तथ्य का लाभ उठाने जा रहे हैं कि एक स्थिर कंस्ट्रक्टर प्रत्येक बंद प्रकार के लिए ठीक एक बार चलता है, यानी SomeGenericType<int> के लिए एक कॉल और SomeGenericType<double> के लिए दूसरी कॉल होगी। उदाहरण:

class SomeGenericType<T>
{
    static SomeGenericType()
    {
        Program.RegisterClosedType(typeof(SomeGenericType<T>));    
    }
}

अंतिम कार्यक्रम:

class Program
{
    static public List<string> _closedTypes = new List<string>();

    static public void RegisterClosedType(Type t)
    {
        _closedTypes.Add(String.Format("{0}<{1}>",
            t.Name.Split('`')[0],
            String.Join(",", t.GenericTypeArguments.Select(q => q.Name))
            ));
    }

    static public void Main()
    {
        var d = new SomeGenericType<double>();
        var i = new SomeGenericType<int>();

        DumpClosedTypes();
    }

    static void DumpClosedTypes()
    {
        foreach (var s in _closedTypes)
        {
            Console.WriteLine(s);
        }
    }
}

इसे चलाएं और आउटपुट है:

SomeGenericType<Double>
SomeGenericType<Int32>
1
John Wu 9 सितंबर 2017, 05:03

इसे रनटाइम पर करना संभव नहीं है, .NET "इस प्रकार के सभी उदाहरण जो मैंने कभी बनाए हैं" के एक सेट का खुलासा नहीं करता है। तो आपको जो कुछ भी आप स्वयं एकत्र करते हैं उसे एकत्र करने के लिए आपको कुछ कोड को ट्विक करने की आवश्यकता होगी। यहां कुछ विकल्प दिए गए हैं:

खराब विकल्प (जो तकनीकी रूप से आपके प्रश्न को हल करता है):

public class Tracker
{
    public static List<Type> Types = new List<Type>();
}

public class MyThing<T>
{
    static MyThing()
    {
        // This is the static constructor, and is called once for every type
        Tracker.Types.Add(typeof(MyThing<T>));
    }
}

बेहतर विकल्प... प्रकारों को संग्रहीत न करें, बस सीधे जो आप चाहते हैं उसे एकत्र करें:

public class Tracker
{
    public static int NumberOfCallsToFoo;
}

public class MyThing<T>
{
    public void Foo(T item) {
        Tracker.NumberOfCallsToFoo++;
        ...
    }
}
0
user6656930user6656930 9 सितंबर 2017, 04:35