كيف يمكنني زيادة التحميل C # الأسلوب حالات محددة من نوع عام

StackOverflow https://stackoverflow.com/questions/405429

  •  03-07-2019
  •  | 
  •  

سؤال

ويأتي من خلفية C ++، لقد واجهت عقبة مع الحمولة الزائدة على أساس نسخة معينة من نوع عام. وفيما يلي لا يعمل مرة واحدة فقط منذ مثيل من التعليمات البرمجية للفئة Foo<T> يتم إنشاء أي وقت مضى، وذلك داخل Method، ونوع من this هو ببساطة Foo<T>، وليس Foo<A> أو Foo<B> كما كنت أتمنى. في C ++ أنا معتاد على القوالب يتم إنشاء مثيل عن أنواع فريدة من نوعها.

using System.Collections.Generic;
class A
{
    // Concrete class
}

class B
{
    // Concrete class
}

class Bar
{
    public void OverloadedMethod(Foo<A> a) {} // do some A related stuff
    public void OverloadedMethod(Foo<B> b) {} // do some B related stuff
    public void OverloadedMethod(OtherFoo of) {} // do some other stuff

     public void VisitFoo(FooBase fb) { fb.Method(this); }
}

abstract class FooBase
{
    public abstract void Method(Bar b);
}

class Foo<T> : FooBase
{
    // Class that deals with As and Bs in an identical fashion.
    public override void Method(Bar b)
    {
        // Doesn't compile here
        b.OverloadedMethod(this);
    }
}

class OtherFoo : FooBase
{
    public override void Method(Bar b)
    {
        b.OverloadedMethod(this);
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<FooBase> ListOfFoos = new List<FooBase>();
        ListOfFoos.Add(new OtherFoo());
        ListOfFoos.Add(new Foo<A>());
        ListOfFoos.Add(new Foo<B>());

        Bar b = new Bar();
        foreach (FooBase fb in ListOfFoos)
            b.VisitFoo(fb);
        // Hopefully call each of the Bar::Overloaded methods
    }
}

هل هناك طريقة للحصول على شيء مثل هذا العمل في C #؟ فما استقاموا لكم فاستقيموا بدلا يكن لديك لتكرار التعليمات البرمجية في فو كصفوف منفصلة لكل نوع أريد أن استخدامه ل.

وتحرير: نأمل أن هذا هو أكثر وضوحا قليلا.

هل كانت مفيدة؟

المحلول

وهذا يعمل في حالة ثابتة. فإن التعامل مع وظائف سبيل المثال أن يكون قليلا أكثر تعقيدا. هذا آخر من جون السكيت قد توفر طريقة معقولة للتعامل مع وسائل المثال.

class Program
{
    static void Main(string[] args)
    {
        var testA = new Foo<A>();
        testA.Method();
        var testB = new Foo<B>();
        testB.Method();
        Console.ReadLine();
        var testString = new Foo<string>(); //Fails
        testString.Method(); 
        Console.ReadLine();
    }
}

class A { }
class B { }
class Bar
{
    public static void OverloadedMethod(Foo<A> a)
    {
        Console.WriteLine("A");
    }
    public static void OverloadedMethod(Foo<B> b)
    {
        Console.WriteLine("B");
    }
}
class Foo<T>
{
    static Foo()
    {
        overloaded = (Action<Foo<T>>)Delegate.CreateDelegate(typeof(Action<Foo<T>>), typeof(Bar).GetMethod("OverloadedMethod", new Type[] { typeof(Foo<T>) }));
    }

    public void Method()
    {
        overloaded(this);
    }

    private static readonly Action<Foo<T>> overloaded;
}

نصائح أخرى

وأنا الآن الحصول على قطعة كاملة حقا من التعليمات البرمجية التي يوضح المشكلة. ملاحظة للOP: من فضلك حاول ترجمة التعليمات البرمجية الخاصة بك قبل نشرها. وكانت هناك مجموعة من الأشياء كان علي أن أفعل للحصول على هذا بكثير. انها جيدة لجعله أسهل ما يمكن لأشخاص آخرين لمساعدتك. أنا أيضا إزالة مجموعة من البتات الدخيلة. OtherFoo غير ذات الصلة حقا هنا، ولا هو FooBase.

class A {}
class B {}

class Bar
{
    public static void OverloadedMethod(Foo<A> a) { }
    public static void OverloadedMethod(Foo<B> b) { }
}

class Foo<T>
{
    // Class that deals with As and Bs in an identical fashion.
    public void Method()
    {
        // Doesn't compile here
        Bar.OverloadedMethod(this);
    }
}

نعم، هذا لا ترجمة. ماذا تتوقع أن تفعل بالضبط؟ نضع في اعتبارنا أن القرار الزائد يتم تنفيذ في <م> تجميع الوقت ، لا وقت التنفيذ. كما يقول fallen888، هل يمكن أن يلقي واستدعاء الأسلوب المناسب زائد - ولكن أي من الزائدة اثنين تتوقع المترجم لاختيار خلاف ذلك؟ ماذا تريد أن تفعل مع Foo<string> بدلا من Foo<A> أو Foo<B>؟

وهذا كله يذهب لإثبات أن الوراثة. NET هي في الواقع تختلف كثيرا عن قوالب C ++، وبطبيعة الحال ...

ولم أحاول ذلك، ولكن على ما يبدو يجب أن تكون قادرة على تحقيق ما تريد عن طريق A & B visitable (على سبيل المثال مع نمط الزوار احلقي).

وتحرير: لست متأكدا من أن تتمكن من إكمال هذا كما كنت تحاول. لقد حاولت كل أنواع الحيل لمحاولة الحصول على هذا العمل ولا يمكن الحصول عليها للترجمة. أفضل ما يمكن فعله هو سحب استدعاء الأسلوب خارج صفي الجنيسة. إذا استدعاء الأسلوب الخاص بك هو خارج، ثم يمكنك تحديد على وجه التحديد ما هو T في عام. ومع ذلك، داخل الأسلوب، في وقت الترجمة والمترجم لا يعرف ما T سيكون لذلك لا يعرف أي مثقلة طريقة للاتصال به. الطريقة الوحيدة التي يمكنني أن أرى حول هذه هي استخدام مفتاح لتحديد نوع T يدويا وتحديد الزائد للاتصال.

وأفضل ما يمكن القيام به هو هذا، وهي ليست تماما ما كنت بعد، ولكن يمكن استخدامها لتأثير مماثل:

class Stuff<T>
{
    public T value { get; set; }
}

class Program
{
    static void DummyFunc(Stuff<int> inst)
    {
        Console.WriteLine("Stuff<int>: {0}", inst.value.ToString());
    }

    static void DummyFunc(Stuff<string> inst)
    {
        Console.WriteLine("Stuff<string>: {0}", inst.value);
    }
    static void DummyFunc(int value)
    {
        Console.WriteLine("int: {0}", value.ToString());
    }
    static void DummyFunc(string value)
    {
        Console.WriteLine("string: {0}", value);
    }
    static void Main(string[] args)
    {
        var a = new Stuff<string>();
        a.value = "HelloWorld";

        var b = new Stuff<int>();
        b.value = 1;

        var c = "HelloWorld";
        var d = 1;

        DummyFunc(a);
        DummyFunc(b);
        DummyFunc(c);
        DummyFunc(d);
    }
}

ووحصلت الإخراج:

Stuff<string>: HelloWorld
Stuff<int>: 1
string: HelloWorld
int: 1

ولقد حصلت على أربع وظائف مثقلة الرجوع اثنين الرجوع فئات عامة (واحد لكثافة واحدة لسلسلة) واثنين من الرجوع أنواع العادية (واحد لكثافة واحدة لسلسلة) وهذا كله يعمل على ما يرام ... هل هذا ما كنت "إعادة بعد؟

وتحرير: لا يبدو أن المشكلة تكون مع الدعوة الطرق فوق طاقتها، عليها أن تفعل مع foreach الخاصة بك والتي تحاول تحويل جميع العناصر الموجودة في القائمة إلى نفس نوع الأول من أجل الرجوع إلى طريقة طاقتها. والعنصر الأول الذي لا يتوافق مع أن التعريف الدقيق يسبب الترجمة الخاص بك إلى فشل.

وكنت آمل أن تجد أسهل طريقة للقيام بذلك ولكن الآن انا ذاهب مع هذا:

واستبدال الطبقة Foo<T> مع هذه الفئات:

abstract class Foo<T> : FooBase
{
    // Class that deals with As and Bs in an identical fashion.
}

class Foo_A : Foo<A>
{
    public override void Method(Bar b)
    {
        b.OverloadedMethod(this);
    }
}

class Foo_B : Foo<B>
{
    public override void Method(Bar b)
    {
        // Doesn't compile here
        b.OverloadedMethod(this);
    }
}

وتغيير التجسيدات إلى

List<FooBase> ListOfFoos = new List<FooBase>();
ListOfFoos.Add(new OtherFoo());
ListOfFoos.Add(new Foo_A());
ListOfFoos.Add(new Foo_B());

وهذا على الأقل لا يتطلب dublicating التعليمات البرمجية في Foo<T>، وتتطلب مني فقط على إحالة الصانعين.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top