සාමාන්‍ය ක්‍රමයක් ඇමතීමට මා පරාවර්තනය භාවිතා කරන්නේ කෙසේද?


1082

සම්පාදක වේලාවේදී පරාමිතිය නොදන්නා නමුත් ක්‍රියාකාරී වේලාවේදී ගතිකව ලබා ගන්නා විට සාමාන්‍ය ක්‍රමයක් ඇමතීමට හොඳම ක්‍රමය කුමක්ද?

පහත දැක්වෙන නියැදි කේතය සලකා බලන්න - Example()ක්‍රමය තුළ, විචල්‍යයේ ගබඩා කර ඇති දේ GenericMethod<T>()භාවිතා කිරීමට වඩාත් සංක්ෂිප්ත ක්‍රමය කුමක්ද?TypemyType

public class Sample
{
    public void Example(string typeName)
    {
        Type myType = FindType(typeName);

        // What goes here to call GenericMethod<T>()?
        GenericMethod<myType>(); // This doesn't work

        // What changes to call StaticMethod<T>()?
        Sample.StaticMethod<myType>(); // This also doesn't work
    }

    public void GenericMethod<T>()
    {
        // ...
    }

    public static void StaticMethod<T>()
    {
        //...
    }
}

7
මම ජොන්ගේ විසඳුම අත්හදා බැලූ අතර මගේ පන්තියේ සාමාන්‍ය ක්‍රමය ප්‍රසිද්ධ කරන තුරු එය ක්‍රියාත්මක කිරීමට නොහැකි විය. මම දන්නවා තවත් ජෝන් පිළිතුරු දුන්නේ ඔබ බන්ධන ෆ්ලෑග් නියම කළ යුතු නමුත් මෙය උදව් නොකළ බවයි.
naskew

12
පුද්ගලික / අභ්‍යන්තර ක්‍රමය ලබා ගැනීම සඳහා ඔබට පමණක් අවශ්‍ය BindingFlags.Instanceනොවේ BindingFlags.NonPublic.
ලාර්ස් කෙමන්

2
මෙම ප්‍රශ්නයේ නවීන අනුවාදය: stackoverflow.com/q/2433436/103167
Ben Voigt

Et පීටර් මෝර්ටෙන්සන් - fyi මම '?' ඉංග්‍රීසි නොවන කොටස් (C #) කොටස් වලින් වෙන් කිරීමට; IMHO අවකාශය ඉවත් කිරීමෙන් එය පෙනෙන්නේ කෙසේද? කේතයේ කොටසකි. කේතයක් නොතිබුනේ නම්, අවකාශය ඉවත් කිරීමට මම නිසැකවම එකඟ වෙමි, නමුත් මේ අවස්ථාවේ දී ...
බෙවන්

අපට සාමාන්‍ය ක්‍රමයක් නිර්වචනය කර GetMethod ක්‍රමය භාවිතා කර සාමාන්‍ය ක්‍රමයේ සියලු තොරතුරු ලබාගෙන එය භාවිතා කළ හැකිය.
elnaz jangi

Answers:


1152

ආරම්භ කිරීම සඳහා ක්‍රමවේදය ලබා ගැනීම සඳහා ඔබ පරාවර්තනය භාවිතා කළ යුතුය, ඉන්පසු MakeGenericMethod සමඟ වර්ග තර්ක ලබා දීමෙන් එය “ සාදන්න” :

MethodInfo method = typeof(Sample).GetMethod(nameof(Sample.GenericMethod));
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

ස්ථිතික ක්‍රමයක් සඳහා, nullපළමු තර්කය ලෙස සම්මත කරන්න Invoke. එය සාමාන්‍ය ක්‍රම සමඟ සම්බන්ධයක් නැත - එය සාමාන්‍ය පරාවර්තනයකි.

සටහන් කර ඇති පරිදි, C # 4 භාවිතයෙන් මෙය බොහෝ සරල ය dynamic- ඔබට වර්ග අනුමාන කිරීම් භාවිතා කළ හැකි නම්, ඇත්ත වශයෙන්ම. ප්‍රශ්නයේ නිශ්චිත උදාහරණය වැනි, අනුමාන කිරීම් නොමැති අවස්ථාවන්හිදී එය උදව් නොකරයි.


92
+1; GetMethod()පොදු නිදර්ශන ක්‍රම පෙරනිමියෙන් පමණක් සලකන බව සලකන්න , එවිට ඔබට අවශ්‍ය විය හැකි BindingFlags.Staticඅතර / හෝ BindingFlags.NonPublic.

20
ධජ වල නිවැරදි සංයෝජනය BindingFlags.NonPublic | BindingFlags.Instance(සහ විකල්ප වශයෙන් BindingFlags.Static).
ලාර්ස් කෙමන්

4
ස්ථිතික ක්‍රම සමඟ මෙය කරන්නේ කෙසේදැයි මෙම ප්‍රශ්නයේ කැපී පෙනෙන ඩුප් එකක් ලබා ගැනීම ප්‍රශ්නයක් වන අතර තාක්‍ෂණිකව ද මෙහි ප්‍රශ්නය මතු වේ. ස්ථිතික ක්‍රම ඇමතීමේදී ඉන්වොක් () හි පළමු පරාමිතිය අහෝසි විය යුතුය. පළමු පරාමිතිය අවශ්‍ය වන්නේ නිදර්ශන ක්‍රම ඇමතීමේදී පමණි.
ක්‍රිස් මොස්චිනි

2
H ක්‍රිස්මොස්චිනි: පිළිතුරට එය එකතු කරන ලදි.
ජෝන් ස්කීට්

2
zgzou: මම පිළිතුරට යමක් එකතු කර ඇත්තෙමි - නමුත් ප්‍රශ්නයේ ඇති සාමාන්‍ය ක්‍රම ඇමතීම සඳහා , dynamicඋපකාරක නොවන බව සලකන්න . (වර්ගය තර්කය තීරණය කිරීම සඳහා සම්පාදකයාට භාවිතා කළ හැකි තර්ක නොමැත.)
ජෝන් ස්කීට්

172

මුල් පිළිතුරට එකතු කිරීමක් පමණි. මෙය ක්‍රියාත්මක වන අතර:

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

ඔබට සම්පාදක කාල පරීක්ෂාව අහිමි වීමද ටිකක් භයානක ය GenericMethod. ඔබ පසුව ප්‍රතිනිර්මාණය කර නැවත නම් කිරීමක් කළහොත් GenericMethod, මෙම කේතය නොපෙනෙන අතර ධාවන වේලාවේදී එය අසාර්ථක වනු ඇත. එසේම, එකලස් කිරීමේ පසු සැකසුම් තිබේ නම් (නිදසුනක් ලෙස භාවිතයට නොගත් ක්‍රම / පන්ති අපැහැදිලි කිරීම හෝ ඉවත් කිරීම) මෙම කේතයද කැඩී යා හැක.

එබැවින්, ඔබ සම්පාදනය කරන වේලාවට සම්බන්ධ වන ක්‍රමය ඔබ දන්නේ නම් සහ මෙය මිලියන ගණනක් නොකියයි, එබැවින් ඉහළින් වැඩ කිරීම වැදගත් නොවේ, මම මෙම කේතය පහත පරිදි වෙනස් කරමි:

Action<> GenMethod = GenericMethod<int>;  //change int by any base type 
                                          //accepted by GenericMethod
MethodInfo method = this.GetType().GetMethod(GenMethod.Method.Name);
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

එතරම් ලස්සන නොවූවත්, ඔබට මෙහි සම්පාදිත කාල සටහනක් ඇති අතර GenericMethod, ඔබ ප්‍රතිනිර්මාණය, මකා දැමීම හෝ යමක් කළහොත් GenericMethod, මෙම කේතය දිගටම ක්‍රියාත්මක වනු ඇත, නැතහොත් අවම වශයෙන් සම්පාදනය කරන වේලාවේදී බිඳී යනු ඇත (නිදසුනක් ලෙස ඔබ ඉවත් කරන්නේ නම් GenericMethod).

එය කළ හැකි වෙනත් ක්‍රමයක් වනුයේ නව ආවරණ පංතියක් නිර්මාණය කිරීම සහ එය හරහා නිර්මාණය කිරීමයි Activator. මීට වඩා හොඳ ක්‍රමයක් තිබේදැයි මම නොදනිමි.


5
ක්‍රමවේදයක් ඇමතීමට පරාවර්තනය භාවිතා කරන අවස්ථාවන්හිදී, ක්‍රමයේ නම වෙනත් ක්‍රමයකින් සොයා ගැනීම සාමාන්‍ය දෙයකි. ක්‍රමයේ නම කල්තියා දැන ගැනීම සාමාන්‍ය දෙයක් නොවේ.
බෙවන්

13
හොඳයි, පරාවර්තනයේ පොදු භාවිතයන් සඳහා මම එකඟ වෙමි. නමුත් මුල්ම ප්‍රශ්නය වූයේ "GenericMethod <myType> ()" අමතන්නේ කෙසේද යන්නයි. එම වාක්‍ය ඛණ්ඩයට ඉඩ දී ඇත්නම් අපට GetMethod () කිසිසේත් අවශ්‍ය නොවේ. නමුත් "GenericMethod <myType>" මම ලියන්නේ කෙසේද යන ප්‍රශ්නයට, GenericMethod සමඟ සම්පාදක-කාල සබැඳිය නැතිවීම වළක්වා ගත හැකි ක්‍රමයක් පිළිතුරට ඇතුළත් විය යුතු යැයි මම සිතමි.දැන් මෙම ප්‍රශ්නය පොදු නම් හෝ මම නොදනිමි, නමුත් මම ඊයේ මෙම නිශ්චිත ප්රශ්නයක් තිබුණේ, සහ මම මේ ප්රශ්නය ගොඩ බසින ඇයි කියලා නම් අපි දන්නවා.
ඒඩ්රියන් Gallero

20
GenMethod.Method.GetGenericMethodDefinition()වෙනුවට ඔබට කළ හැකිය this.GetType().GetMethod(GenMethod.Method.Name). එය තරමක් පිරිසිදු හා බොහෝ විට ආරක්ෂිතයි.
ඩැනියෙල් කැසිඩි

ඔබගේ නියැදියේ "myType" යන්නෙන් අදහස් කරන්නේ කුමක්ද?
සංවර්ධක

37
දැන් ඔබට භාවිතා කළ හැකියnameof(GenericMethod)
dmigo

143

ධාවන වේලාවේදී පමණක් දන්නා වර්ග පරාමිතියක් සහිත සාමාන්‍ය ක්‍රමයක් ඇමතීමෙන් dynamicපරාවර්තන API වෙනුවට වර්ගයක් භාවිතා කිරීමෙන් බොහෝ සරල කළ හැකිය .

මෙම තාක්ෂණය භාවිතා කිරීම සඳහා වර්ගය සත්‍ය වස්තුවෙන් දැනගත යුතුය ( Typeපන්තියේ අවස්ථාවක් පමණක් නොවේ ). එසේ නොමැතිනම්, ඔබට එම වර්ගයේ වස්තුවක් නිර්මාණය කිරීමට හෝ සම්මත පරාවර්තන API විසඳුම භාවිතා කිරීමට සිදුවේ . Activator.CreateInstance ක්‍රමය භාවිතා කිරීමෙන් ඔබට වස්තුවක් නිර්මාණය කළ හැකිය .

ඔබට සාමාන්‍ය ක්‍රමයක් ඇමතීමට අවශ්‍ය නම්, “සාමාන්‍ය” භාවිතයේදී එහි වර්ගය අනුමාන කළ හැකිව තිබුනේ නම්, එය හුදෙක් නොදන්නා වර්ගයේ වස්තුව වාත්තු කිරීම සඳහා පැමිණේ dynamic. මෙන්න උදාහරණයක්:

class Alpha { }
class Beta { }
class Service
{
    public void Process<T>(T item)
    {
        Console.WriteLine("item.GetType(): " + item.GetType()
                          + "\ttypeof(T): " + typeof(T));
    }
}

class Program
{
    static void Main(string[] args)
    {
        var a = new Alpha();
        var b = new Beta();

        var service = new Service();
        service.Process(a); // Same as "service.Process<Alpha>(a)"
        service.Process(b); // Same as "service.Process<Beta>(b)"

        var objects = new object[] { a, b };
        foreach (var o in objects)
        {
            service.Process(o); // Same as "service.Process<object>(o)"
        }
        foreach (var o in objects)
        {
            dynamic dynObj = o;
            service.Process(dynObj); // Or write "service.Process((dynamic)o)"
        }
    }
}

මෙන්න මෙම වැඩසටහනේ ප්‍රතිදානය:

item.GetType(): Alpha    typeof(T): Alpha
item.GetType(): Beta     typeof(T): Beta
item.GetType(): Alpha    typeof(T): System.Object
item.GetType(): Beta     typeof(T): System.Object
item.GetType(): Alpha    typeof(T): Alpha
item.GetType(): Beta     typeof(T): Beta

Processසම්මත සම්මත තර්කයෙහි සත්‍ය වර්ගය ( GetType()ක්‍රමය භාවිතා කිරීමෙන් ) සහ සාමාන්‍ය පරාමිතියේ වර්ගය ( typeofක්‍රියාකරු භාවිතා කිරීමෙන් ) ලියන සාමාන්‍ය උදාහරණ ක්‍රමයකි .

වස්තු තර්කය dynamicටයිප් කිරීමට වාත්තු කිරීමෙන්, ධාවන කාලය තෙක් පරාමිතිය සැපයීම කල් දැමුවෙමු. වූ විට Processක්රමය සමග හැඳින්වේ dynamicතර්කය නම් සම්පාදකවරයා මෙම තර්කය වර්ගය ගැන සැලකිලිමත් වෙන්නේ නැහැ. සම්පාදකයා විසින් ජනනය කරන කේතයක් ක්‍රියාත්මක වන වේලාවේදී සම්මත සම්මත තර්ක (පරාවර්තනය භාවිතා කරමින්) පරීක්ෂා කර ඇමතීමට හොඳම ක්‍රමය තෝරන්න. මෙන්න ඇත්තේ මෙම එක් සාමාන්‍ය ක්‍රමයක් පමණි, එබැවින් එය නිසි ආකාරයේ පරාමිතියක් සමඟ ආයාචනය කරයි.

මෙම උදාහරණයේ දී, ප්‍රතිදානය ඔබ ලියා ඇති ආකාරයටම වේ:

foreach (var o in objects)
{
    MethodInfo method = typeof(Service).GetMethod("Process");
    MethodInfo generic = method.MakeGenericMethod(o.GetType());
    generic.Invoke(service, new object[] { o });
}

ගතික වර්ගයක් සහිත අනුවාදය අනිවාර්යයෙන්ම කෙටි හා ලිවීමට පහසුය. මෙම ශ්‍රිතය කිහිප වතාවක් ඇමතීමේ ක්‍රියාකාරිත්වය ගැනද ඔබ කරදර නොවිය යුතුය. එකම වර්ගයේ තර්ක ඉදිරි ඇමතුමක් වේගවත් ස්තුති කළ යුතු ගබඩා DLR දී යාන්ත්රණයක්. ඇත්ත වශයෙන්ම, ඔබට නියෝජිතයින් හැඹිලිගත කරන කේතයක් ලිවිය හැකිය, නමුත් dynamicවර්ගය භාවිතා කිරීමෙන් ඔබට මෙම හැසිරීම නොමිලේ ලැබේ.

ඔබට ඇමතීමට අවශ්‍ය සාමාන්‍ය ක්‍රමයට පරාමිතිගත වර්ගයක තර්කයක් නොමැති නම් (එබැවින් එහි වර්ග පරාමිතිය අනුමාන කළ නොහැක) එවිට ඔබට පහත දැක්වෙන උදාහරණයේ දී මෙන් සාමාන්‍ය ක්‍රමයේ ආයාචනය උපකාරක ක්‍රමයකින් ඔතා ගත හැකිය:

class Program
{
    static void Main(string[] args)
    {
        object obj = new Alpha();

        Helper((dynamic)obj);
    }

    public static void Helper<T>(T obj)
    {
        GenericMethod<T>();
    }

    public static void GenericMethod<T>()
    {
        Console.WriteLine("GenericMethod<" + typeof(T) + ">");
    }
}

වර්ගයේ ආරක්ෂාව වැඩි කිරීම

dynamicපරාවර්තන ඒපීඅයි භාවිතා කිරීම වෙනුවට ආදේශකයක් ලෙස වස්තුව භාවිතා කිරීම ඇත්තෙන්ම විශිෂ්ට දෙය නම්, ඔබ ක්‍රියාත්මක වන තෙක් ඔබ නොදන්නා මෙම විශේෂිත වර්ගයේ සංයුක්ත කාලය පරීක්ෂා කිරීම පමණක් ඔබට අහිමි වීමයි. වෙනත් තර්ක සහ ක්‍රමයේ නම සම්පාදකයා විසින් සුපුරුදු පරිදි ස්ථිතිකව විශ්ලේෂණය කරනු ලැබේ. ඔබ තවත් තර්ක ඉවත් කිරීම හෝ එකතු කිරීම, ඒවායේ වර්ග වෙනස් කිරීම හෝ ක්‍රමයේ නම නැවත නම් කිරීම මඟින් ඔබට සම්පාදක කාල දෝෂයක් ලැබෙනු ඇත. ඔබ ක්‍රමයේ නම නූලක් Type.GetMethodලෙසත්, වස්තු අරාව ලෙස තර්ක කළහොත්ත් මෙය සිදු නොවේ MethodInfo.Invoke.

සම්පාදනය කරන වේලාවේදී (අදහස් දැක්වූ කේතය) සහ වෙනත් ධාවන වේලාවේදී සමහර දෝෂ හඳුනාගත හැකි ආකාරය නිරූපණය කරන සරල උදාහරණයකි. කුමන ක්‍රමය ඇමතිය යුතුද යන්න විසඳීමට ඩීඑල්ආර් උත්සාහ කරන ආකාරය ද එය පෙන්වයි.

interface IItem { }
class FooItem : IItem { }
class BarItem : IItem { }
class Alpha { }

class Program
{
    static void Main(string[] args)
    {
        var objects = new object[] { new FooItem(), new BarItem(), new Alpha() };
        for (int i = 0; i < objects.Length; i++)
        {
            ProcessItem((dynamic)objects[i], "test" + i, i);

            //ProcesItm((dynamic)objects[i], "test" + i, i);
            //compiler error: The name 'ProcesItm' does not
            //exist in the current context

            //ProcessItem((dynamic)objects[i], "test" + i);
            //error: No overload for method 'ProcessItem' takes 2 arguments
        }
    }

    static string ProcessItem<T>(T item, string text, int number)
        where T : IItem
    {
        Console.WriteLine("Generic ProcessItem<{0}>, text {1}, number:{2}",
                          typeof(T), text, number);
        return "OK";
    }
    static void ProcessItem(BarItem item, string text, int number)
    {
        Console.WriteLine("ProcessItem with Bar, " + text + ", " + number);
    }
}

මෙහිදී අපි නැවතත් යම් ක්‍රමයක් ක්‍රියාත්මක කරන්නේ තර්කය dynamicවර්ගයට දමමිනි. පළමු තර්කයේ වර්ගය සත්‍යාපනය කිරීම පමණක් ධාවන කාලයට කල් දමනු ලැබේ. ඔබ අමතන ක්‍රමයේ නම නොපවතින නම් හෝ වෙනත් තර්ක අවලංගු නම් (වැරදි තර්ක ගණනක් හෝ වැරදි වර්ග) ඔබට සම්පාදක දෝෂයක් ලැබෙනු ඇත.

ඔබ dynamicතර්කය ක්‍රමයකට යොමු කළ විට මෙම ඇමතුම මෑතකදී බැඳී ඇත. ක්‍රමයේ අධි බර විභේදනය ක්‍රියාත්මක වන වේලාවේදී සිදු වන අතර හොඳම අධි බර තෝරා ගැනීමට උත්සාහ කරයි. එබැවින් ඔබ වර්ගයේ ProcessItemවස්තුවක් සමඟ ක්‍රමයට ආයාචනා කරන්නේ නම්, ඔබ BarItemඇත්ත වශයෙන්ම සාමාන්‍ය නොවන ක්‍රමය ලෙස හඳුන්වනු ඇත, මන්ද එය මෙම වර්ගයට වඩා හොඳ ගැලපීමක් වන බැවිනි. කෙසේ වෙතත්, ඔබ Alphaවර්ගයේ තර්කයක් සම්මත කරන විට ඔබට ධාවන කාල දෝෂයක් ලැබෙනු ඇත, මන්ද මෙම වස්තුව හැසිරවිය හැකි ක්‍රමයක් නොමැති නිසාය (සාමාන්‍ය ක්‍රමයකට අවහිරතා ඇති where T : IItemඅතර Alphaපන්තිය මෙම අතුරු මුහුණත ක්‍රියාත්මක නොකරයි). නමුත් සමස්ත කාරණය එයයි. මෙම ඇමතුම වලංගු බවට තොරතුරු සම්පාදකයා සතුව නොමැත. ක්‍රමලේඛකයෙකු ලෙස ඔබ මෙය දන්නා අතර මෙම කේතය දෝෂයකින් තොරව ක්‍රියාත්මක වන බවට වග බලා ගත යුතුය.

ආපසු එන වර්ගය ගොචා

ඔබ ගතික වර්ගය ක පරාමිතිය සමඟ-අඩුව නොවන ක්රමය ඉල්ලා විට, සිය ආපසු වර්ගය බොහෝ විට කරනු විය dynamic . එබැවින් ඔබ පෙර උදාහරණය මෙම කේතයට වෙනස් කළහොත්:

var result = ProcessItem((dynamic)testObjects[i], "test" + i, i);

එවිට ප්‍රති result ල වස්තුවේ වර්ගය වනු ඇත dynamic. මෙයට හේතුව සම්පාදකයා සැමවිටම හඳුන්වන්නේ කුමන ක්‍රමයද යන්නයි. ශ්‍රිත ඇමතුමේ ආපසු පැමිණීමේ වර්ගය ඔබ දන්නේ නම්, ඔබ එය ව්‍යංගයෙන් අවශ්‍ය වර්ගයට පරිවර්තනය කළ යුතුය, එවිට ඉතිරි කේතය ස්ථිතිකව ටයිප් කරනු ලැබේ:

string result = ProcessItem((dynamic)testObjects[i], "test" + i, i);

වර්ගය නොගැලපේ නම් ඔබට ධාවන කාල දෝෂයක් ලැබෙනු ඇත.

ඇත්ත වශයෙන්ම, ඔබ පෙර උදාහරණයේ ප්‍රති result ල අගය ලබා ගැනීමට උත්සාහ කළහොත් දෙවන ලූප් පුනරාවර්තනයේදී ඔබට ධාවන කාල දෝෂයක් ලැබෙනු ඇත. මෙයට හේතුව ඔබ අවලංගු ශ්‍රිතයක ප්‍රතිලාභ අගය සුරැකීමට උත්සාහ කිරීමයි.


"කෙසේ වෙතත්, ඔබ ඇල්ෆා වර්ගයේ තර්කය සම්මත කරන විට මෙම වස්තුව හැසිරවිය හැකි ක්‍රමයක් නොමැති නිසා ඔබට ධාවන කාල දෝෂයක් ඇති වේ." මම var a = new Alpha () ProcessItem (a, "test" + i , i) “සාමාන්‍ය ක්‍රියාවලි අයිතමය” ප්‍රතිදානය කරමින් සාමාන්‍ය ප්‍රොසෙස් අයිටම් ක්‍රමය මෙය effectively ලදායී ලෙස හැසිරෙන්නේ නැත්තේ ඇයි?
ඇලෙක්ස් එඩෙල්ස්ටයින්

Lex ඇලෙක්ස් එඩෙල්ස්ටයින් මම මගේ පිළිතුර සංස්කරණය කළේ ටිකක් පැහැදිලි කර ගැනීමටයි. එයට හේතුව ජනක ProcessItemක්‍රමයට සාමාන්‍ය අවහිරතා ඇති අතර IItemඅතුරු මුහුණත ක්‍රියාත්මක කරන වස්තුව පමණක් පිළිගැනීමයි . ඔබ ඇමතුමක් ලබා දෙන විට ProcessItem(new Aplha(), "test" , 1);හෝ ProcessItem((object)(new Aplha()), "test" , 1);ඔබට සම්පාදක දෝෂයක් ඇති නමුත් dynamicඔබට වාත්තු කිරීමේදී එම චෙක්පත ධාවන කාලයට කල් දමන්න.
මාරියස් පවෙල්ස්කි

විශිෂ්ට පිළිතුරක් සහ පැහැදිලි කිරීමක් මට හොඳින් ක්‍රියාත්මක වේ. පිළිගත් පිළිතුරට වඩා හොඳය, ලිවීමට කෙටි, වඩා කාර්ය සාධනය සහ ආරක්ෂිතයි.
ygoe

17

C # 4.0 සමඟ, DLR හට ධාවන කාල වර්ග භාවිතා කර එය ඇමතිය හැකි බැවින් පරාවර්තනය අවශ්‍ය නොවේ. ඩීඑල්ආර් පුස්තකාලය භාවිතා කිරීම ගතිකව වේදනාකාරී වන හෙයින් (ඔබ වෙනුවෙන් සී # සම්පාදක උත්පාදක කේතය වෙනුවට), විවෘත මූලාශ්‍ර රාමුව ඩයිනමයිටි (.net සම්මත 1.5) මඟින් සම්පාදකයා ජනනය කරන ඇමතුම් වලට පහසුවෙන් හැඹිලි ධාවන කාල ප්‍රවේශය ලබා දේ. ඔයා වෙනුවෙන්.

var name = InvokeMemberName.Create;
Dynamic.InvokeMemberAction(this, name("GenericMethod", new[]{myType}));


var staticContext = InvokeContext.CreateStatic;
Dynamic.InvokeMemberAction(staticContext(typeof(Sample)), name("StaticMethod", new[]{myType}));

13

ඒඩ්‍රියන් ගැලෙරෝගේ පිළිතුරට එකතු කිරීම :

වර්ග තොරතුරු වලින් සාමාන්‍ය ක්‍රමයක් ඇමතීමට පියවර තුනක් ඇතුළත් වේ.

TLDR: වර්ග වස්තුවක් සමඟ දන්නා සාමාන්‍ය ක්‍රමයක් ඇමතීමෙන් මෙය කළ හැකිය:

((Action)GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition()
    .MakeGenericMethod(typeof(string))
    .Invoke(this, null);

එහිදී GenericMethod<object>මෙම ක්රමය යන නාමය තබන්න හා ඕනෑම ආකාරයේ තෘප්තිමත් වර්ගීය සීමාවන් බව ය.

(ක්‍රියාව) හැඳින්විය යුතු ක්‍රමයේ අත්සනට ගැලපේ, එනම් ( Func<string,string,int>හෝ Action<bool>)

පියවර 1 යනු සාමාන්‍ය ක්‍රම නිර්වචනය සඳහා MethodInfo ලබා ගැනීමයි

ක්රමය 1: සුදුසු වර්ග හෝ බන්ධන කොඩි සමඟ GetMethod () හෝ GetMethods () භාවිතා කරන්න.

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");

ක්රමය 2: නියෝජිතයෙකු සාදන්න, MethodInfo වස්තුව ලබාගෙන GetGenericMethodDefinition අමතන්න

ක්‍රම අඩංගු පන්තියේ ඇතුළත සිට:

MethodInfo method = ((Action)GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition();

MethodInfo method = ((Action)StaticMethod<object>)
    .Method
    .GetGenericMethodDefinition();

ක්‍රම අඩංගු පන්තියෙන් පිටත සිට:

MethodInfo method = ((Action)(new Sample())
    .GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition();

MethodInfo method = ((Action)Sample.StaticMethod<object>)
    .Method
    .GetGenericMethodDefinition();

C # හි, ක්‍රමයක නම, එනම් “ToString” හෝ “GenericMethod” යන්නෙන් අදහස් කරන්නේ ක්‍රම එකක් හෝ කිහිපයක් අඩංගු විය හැකි ක්‍රම සමූහයකි. ඔබ ක්‍රම පරාමිතීන් වර්ග ලබා දෙන තුරු, ඔබ යොමු කරන්නේ කුමන ක්‍රමයද යන්න නොදනී.

((Action)GenericMethod<object>)නිශ්චිත ක්‍රමයක් සඳහා නියෝජිතයා වෙත යොමු වේ. ((Func<string, int>)GenericMethod<object>) GenericMethod හි වෙනස් අධි බරක් අදහස් කරයි

ක්රමය 3: ක්රම ඇමතුම් ප්රකාශනයක් අඩංගු ලැම්බඩා ප්රකාශනයක් සාදන්න, MethodInfo වස්තුව ලබාගෙන GetGenericMethodDefinition

MethodInfo method = ((MethodCallExpression)((Expression<Action<Sample>>)(
    (Sample v) => v.GenericMethod<object>()
    )).Body).Method.GetGenericMethodDefinition();

මෙය බිඳී යයි

ශරීරය ඔබ අපේක්ෂිත ක්‍රමයට කැඳවීමක් වන ලැම්බඩා ප්‍රකාශනයක් සාදන්න.

Expression<Action<Sample>> expr = (Sample v) => v.GenericMethod<object>();

ශරීරය නිස්සාරණය කර MethodCallExpression වෙත දමන්න

MethodCallExpression methodCallExpr = (MethodCallExpression)expr.Body;

ක්‍රමයෙන් සාමාන්‍ය ක්‍රම නිර්වචනය ලබා ගන්න

MethodInfo methodA = methodCallExpr.Method.GetGenericMethodDefinition();

පියවර 2 යනු සුදුසු වර්ග (ය) සමඟ සාමාන්‍ය ක්‍රමයක් නිර්මාණය කිරීම සඳහා MakeGenericMethod අමතන්න.

MethodInfo generic = method.MakeGenericMethod(myType);

3 වන පියවර සුදුසු තර්ක සමඟ ක්‍රමවේදය ක්‍රියාත්මක කරයි.

generic.Invoke(this, null);

8

කිසිවෙකු " සම්භාව්‍ය පරාවර්තනය " විසඳුම ලබා දී නැත , එබැවින් මෙහි සම්පූර්ණ කේත උදාහරණයක් ඇත:

using System;
using System.Collections;
using System.Collections.Generic;

namespace DictionaryRuntime
{
    public class DynamicDictionaryFactory
    {
        /// <summary>
        /// Factory to create dynamically a generic Dictionary.
        /// </summary>
        public IDictionary CreateDynamicGenericInstance(Type keyType, Type valueType)
        {
            //Creating the Dictionary.
            Type typeDict = typeof(Dictionary<,>);

            //Creating KeyValue Type for Dictionary.
            Type[] typeArgs = { keyType, valueType };

            //Passing the Type and create Dictionary Type.
            Type genericType = typeDict.MakeGenericType(typeArgs);

            //Creating Instance for Dictionary<K,T>.
            IDictionary d = Activator.CreateInstance(genericType) as IDictionary;

            return d;

        }
    }
}

ඉහත DynamicDictionaryFactoryපන්තියට ක්‍රමයක් ඇත

CreateDynamicGenericInstance(Type keyType, Type valueType)

එය නිර්මාණය හා IDictionary උදාහරණයක් නැවත සිය යතුරු හා අගයන් වර්ග හරියටම ඇමතුමක් මත නිශ්චිතව දක්වා ඇත keyTypeහා valueType.

ක්ෂණිකව භාවිතා කිරීමට මෙම ක්‍රමය අමතන්නේ කෙසේද යන්න පිළිබඳ සම්පූර්ණ උදාහරණය මෙන්නDictionary<String, int> :

using System;
using System.Collections.Generic;

namespace DynamicDictionary
{
    class Test
    {
        static void Main(string[] args)
        {
            var factory = new DictionaryRuntime.DynamicDictionaryFactory();
            var dict = factory.CreateDynamicGenericInstance(typeof(String), typeof(int));

            var typedDict = dict as Dictionary<String, int>;

            if (typedDict != null)
            {
                Console.WriteLine("Dictionary<String, int>");

                typedDict.Add("One", 1);
                typedDict.Add("Two", 2);
                typedDict.Add("Three", 3);

                foreach(var kvp in typedDict)
                {
                    Console.WriteLine("\"" + kvp.Key + "\": " + kvp.Value);
                }
            }
            else
                Console.WriteLine("null");
        }
    }
}

ඉහත කොන්සෝල යෙදුම ක්‍රියාත්මක වූ විට, අපට නිවැරදි, අපේක්ෂිත ප්‍රති result ලය ලැබේ:

Dictionary<String, int>
"One": 1
"Two": 2
"Three": 3

2

ග්‍රැක්ස්ගේ පිළිතුර මත පදනම්ව මෙය මගේ ශත 2 යි , නමුත් සාමාන්‍ය ක්‍රමයක් සඳහා පරාමිති දෙකක් අවශ්‍ය වේ.

උපකාරක පන්තියක ඔබේ ක්‍රමය පහත පරිදි අර්ථ දක්වා ඇතැයි උපකල්පනය කරන්න:

public class Helpers
{
    public static U ConvertCsvDataToCollection<U, T>(string csvData)
    where U : ObservableCollection<T>
    {
      //transform code here
    }
}

මගේ නඩුවේදී, U වර්ගය සැමවිටම නිරීක්ෂණය කළ හැකි එකතු කිරීමේ ගබඩා කරන වස්තුවකි.

මගේ වර්ග පූර්ව නිශ්චිතව දක්වා ඇති හෙයින්, මම මුලින්ම නිර්මාණය කළ හැකි නිරීක්ෂණ එකතුව (යූ) සහ එහි ගබඩා කර ඇති වස්තුව (ටී) නිරූපණය කරන “ව්‍යාජ” වස්තූන් නිර්මාණය කරන අතර ඒවා සාදන්න අමතන විට ඒවායේ වර්ගය ලබා ගැනීමට පහත භාවිතා කරනු ඇත.

object myCollection = Activator.CreateInstance(collectionType);
object myoObject = Activator.CreateInstance(objectType);

ඔබේ සාමාන්‍ය ක්‍රියාකාරිත්වය සොයා ගැනීමට GetMethod අමතන්න:

MethodInfo method = typeof(Helpers).
GetMethod("ConvertCsvDataToCollection");

මෙතෙක්, ඉහත ඇමතුම ඉහත විස්තර කර ඇති දෙයට වඩා බොහෝ සෙයින් සමාන නමුත් ඔබට අවශ්‍ය විටදී පරාමිතීන් කිහිපයක් සම්මත කිරීමට අවශ්‍ය වූ විට කුඩා වෙනසක් ඇත.

ඉහත නිර්මාණය කරන ලද "ව්‍යාජ" වස්තු වර්ග අඩංගු MakeGenericMethod ශ්‍රිතයට ඔබ වර්ගයක් [] පෙළක් යැවිය යුතුය:

MethodInfo generic = method.MakeGenericMethod(
new Type[] {
   myCollection.GetType(),
   myObject.GetType()
});

එය අවසන් වූ පසු, ඉහත සඳහන් කළ පරිදි ඉන්වොක් ක්‍රමය අමතන්න.

generic.Invoke(null, new object[] { csvData });

ඔබ ඉවරයි. චමත්කාරජනකයි!

යාවත්කාලීන කිරීම:

E බෙවන් ඉස්මතු කර ඇති පරිදි, MakeGenericMethod ශ්‍රිතය පරාමිතීන් ලෙස හඳුන්වන විට මට අරාවක් සෑදීමට අවශ්‍ය නොවන අතර වර්ග ලබා ගැනීම සඳහා වස්තුවක් නිර්මාණය කිරීමට අවශ්‍ය නොවන බැවින් වර්ග සෘජුවම මෙම ශ්‍රිතයට යැවිය හැකිය. මගේ නඩුවේදී, වෙනත් පන්තියක පූර්ව නිශ්චිත වර්ග මා සතුව ඇති බැවින්, මම මගේ කේතය පහත පරිදි වෙනස් කළෙමි:

object myCollection = null;

MethodInfo method = typeof(Helpers).
GetMethod("ConvertCsvDataToCollection");

MethodInfo generic = method.MakeGenericMethod(
   myClassInfo.CollectionType,
   myClassInfo.ObjectType
);

myCollection = generic.Invoke(null, new object[] { csvData });

myClassInfo වර්ගයේ ගුණාංග 2 ක් අඩංගු වන Typeඅතර එය ඉදිකිරීම්කරු වෙත ලබා දුන් enum අගය මත පදනම්ව මම ධාවන වේලාවට සකසා ඇති අතර පසුව MakeGenericMethod හි භාවිතා කරන අදාළ වර්ග මට ලබා දෙනු ඇත.

මෙය ඉස්මතු කිරීම ගැන නැවතත් ස්තූතියි.


පරාමිති මූල පදය MakeGenericMethod()තිබිය යුතු තර්ක ඔබට අරාවක් සෑදීමට අවශ්‍ය නැත; වර්ග ලබා ගැනීම සඳහා ඔබට අවස්ථා නිර්මාණය කිරීමට අවශ්‍ය නොවේ - ප්‍රමාණවත් වනු ඇත. methodInfo.MakeGenericMethod(typeof(TCollection), typeof(TObject))
බෙවන්

0

විසින් ජාතිය Enigmativity පිළිතුර - ඉඩ ගේ වගේ, ඔබ දෙක (හෝ ඊට වැඩි), පංති භාර

public class Bar { }
public class Square { }

සහ ප්‍රකාශිත ක්‍රමය Foo<T>සමඟ Barසහ ඇමතීමට ඔබට අවශ්‍යයSquare

public class myClass
{
    public void Foo<T>(T item)
    {
        Console.WriteLine(typeof(T).Name);
    }
}

එවිට ඔබට දිගු කිරීමේ ක්‍රමයක් ක්‍රියාත්මක කළ හැකිය :

public static class Extension
{
    public static void InvokeFoo<T>(this T t)
    {
        var fooMethod = typeof(myClass).GetMethod("Foo");
        var tType = typeof(T);
        var fooTMethod = fooMethod.MakeGenericMethod(new[] { tType });
        fooTMethod.Invoke(new myClass(), new object[] { t });
    }
}

මේ සමඟ, ඔබට සරලවම ආයාචනා කළ හැකිය Foo:

var objSquare = new Square();
objSquare.InvokeFoo();

var objBar = new Bar();
objBar.InvokeFoo();

එය සෑම පන්තියකටම වැඩ කරයි. මෙම අවස්ථාවේදී, එය ප්‍රතිදානය කරනු ඇත:

හතරැස්
තීරුව

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.