سؤال

هل هناك طريقة لتجاوز أنواع عودة في C# ؟ إذا فكيف ، وإذا لا لماذا و ما هي الطريقة الموصى بها من يفعل ذلك ؟

قضيتي هي أن لدي واجهة مع قاعدة مجردة فئة أحفاد ذلك.وأود أن تفعل هذا (طيب لا حقا ، ولكن كمثال!) :

public interface Animal
{
   Poo Excrement { get; }
}

public class AnimalBase
{
   public virtual Poo Excrement { get { return new Poo(); } }
}

public class Dog
{
  // No override, just return normal poo like normal animal
}

public class Cat
{
  public override RadioactivePoo Excrement { get { return new RadioActivePoo(); } }
}

RadioactivePoo بالطبع يرث من Poo.

سبب الرغبة في ذلك هو أن أولئك الذين يستخدمون Cat الكائنات يمكن استخدام Excrement الملكية دون أن يلقي Poo في RadioactivePoo في حين على سبيل المثال Cat يمكن أن يكون لا يزال جزءا من Animal قائمة حيث المستخدمين قد لا يكون بالضرورة على علم أو يهتمون المشعة براز.نأمل أن معنى...

بقدر ما أستطيع أن أرى المترجم لا يسمح بذلك على الأقل.لذا أعتقد أنه من المستحيل.ولكن ماذا تقترح كحل هذا ؟

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

المحلول

ماذا عن قاعدة عامة الفصل ؟

public class Poo { }
public class RadioactivePoo : Poo { }

public class BaseAnimal<PooType> 
    where PooType : Poo, new() {
    PooType Excrement {
        get { return new PooType(); }
    }
}

public class Dog : BaseAnimal<Poo> { }
public class Cat : BaseAnimal<RadioactivePoo> { }

تحرير:حل جديد ، وذلك باستخدام أساليب الإرشاد وعلامة واجهة...

public class Poo { }
public class RadioactivePoo : Poo { }

// just a marker interface, to get the poo type
public interface IPooProvider<PooType> { }

// Extension method to get the correct type of excrement
public static class IPooProviderExtension {
    public static PooType StronglyTypedExcrement<PooType>(
        this IPooProvider<PooType> iPooProvider) 
        where PooType : Poo {
        BaseAnimal animal = iPooProvider as BaseAnimal;
        if (null == animal) {
            throw new InvalidArgumentException("iPooProvider must be a BaseAnimal.");
        }
        return (PooType)animal.Excrement;
    }
}

public class BaseAnimal {
    public virtual Poo Excrement {
        get { return new Poo(); }
    }
}

public class Dog : BaseAnimal, IPooProvider<Poo> { }

public class Cat : BaseAnimal, IPooProvider<RadioactivePoo> {
    public override Poo Excrement {
        get { return new RadioactivePoo(); }
    }
}

class Program { 
    static void Main(string[] args) {
        Dog dog = new Dog();
        Poo dogPoo = dog.Excrement;

        Cat cat = new Cat();
        RadioactivePoo catPoo = cat.StronglyTypedExcrement();
    }
}

هذه الطريقة الكلب والقط يرثون من الحيوان (كما لاحظ في التعليقات ، الحل الأول لم يحفظ الميراث).
من الضروري أن مارك صراحة الطبقات مع علامة واجهة التي هي مؤلمة لكن ربما هذا يمكن أن تعطي لك بعض الأفكار...

تحرير الثاني @Svish:أنا تعديل كود لإظهار explitly أن طريقة التمديد هو عدم تطبيق في أي طريقة من حقيقة أن iPooProvider يرث من BaseAnimal.ماذا تعني "أكثر المكتوبة بشدة"?

نصائح أخرى

وهذا ما يسمى <القوي> نوع الإرجاع التغاير و غير معتمد في C # .NET أو بشكل عام، على الرغم من بعض الناس <وأ href = "http://connect.microsoft.com/VisualStudio/feedback/ ViewFeedback.aspx؟ FeedbackID = 90909 "يختلط =" نوفولو noreferrer "> يرغب .

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

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

ونفس عن الوراثة، والتي إجابات أخرى توحي. وأود أن استخدامها لسبب أفضل من مجرد العودة براز المشع - ولكن هذا مجرد لي

أعرف أن هناك الكثير من الحلول لهذه المشكلة بالفعل ولكن أعتقد أنني قد تأتي مع واحد أن المشكلات مع الحلول القائمة.

لم أكن سعيدا مع بعض الحلول القائمة للأسباب التالية:

  • باولو Tedesco هو الحل الأول: القط والكلب لا تملك قاعدة مشتركة الدرجة.
  • باولو Tedesco الثاني الحل: فمن قليلا معقدة و من الصعب قراءة.
  • دانيال Daranas الحل: يعمل هذا ولكن هذا من شأنه أن تحدث فوضى التعليمات البرمجية الخاصة بك مع الكثير من لا لزوم لها الصب و التصحيح.يؤكد() البيانات.
  • hjb417 حلول: هذا الحل لا تمكنك من الحفاظ على المنطق الخاص في الفئة الأساسية.المنطق هو تافهة جدا في هذا المثال (استدعاء منشئ) ولكن في العالم الحقيقي سبيل المثال لن يكون.

الحل

هذا الحل يجب التغلب على جميع المسائل التي ذكرتها أعلاه باستخدام كل الأدوية وطريقة الاختباء.

public class Poo { }
public class RadioactivePoo : Poo { }

interface IAnimal
{
    Poo Excrement { get; }
}

public class BaseAnimal<PooType> : IAnimal
    where PooType : Poo, new()
{
    Poo IAnimal.Excrement { get { return (Poo)this.Excrement; } }

    public PooType Excrement
    {
        get { return new PooType(); }
    }
}

public class Dog : BaseAnimal<Poo> { }
public class Cat : BaseAnimal<RadioactivePoo> { }

مع هذا الحل لا تحتاج إلى تجاوز أي شيء في الكلب أو القط!هنا بعض نماذج الاستخدام:

Cat bruce = new Cat();
IAnimal bruceAsAnimal = bruce as IAnimal;
Console.WriteLine(bruce.Excrement.ToString());
Console.WriteLine(bruceAsAnimal.Excrement.ToString());

هذا الناتج:"RadioactivePoo" مرتين مما يدل على أن تعدد الأشكال لم يتم كسر.

مزيد من القراءة

  • صريحة واجهة التنفيذ
  • جديد معدل.أنا لم استخدم هذا الحل المبسط ولكن قد تحتاج في أكثر من حل.على سبيل المثال إذا أردت إنشاء واجهة BaseAnimal ثم سوف تحتاج إلى استخدام في التصريح من "PooType البراز".
  • خارج عامة التعديل (التغاير).مرة أخرى لم تستخدم في هذا الحل ولكن إذا كنت تريد أن تفعل شيء مثل العودة MyType<Poo> من IAnimal والعودة MyType<PooType> من BaseAnimal ثم سوف تحتاج إلى أن تكون قادرة على الزهر بين البلدين.

وهناك أيضا هذا الخيار (صريح واجهة التنفيذ)

public class Cat:Animal
{
  Poo Animal.Excrement { get { return Excrement; } }
  public RadioactivePoo Excrement { get { return new RadioactivePoo(); } }
}

وتفقد القدرة على استخدام الطبقة قاعدة لتنفيذ القط، ولكن على الجانب زائد، عليك أن تبقي تعدد الأشكال بين القطة والكلب.

ولكن أشك في التعقد يستحق ذلك.

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

في المثال التالي، وأنا جعل 'الغائط "غير افتراضية ولكن توفير ExcrementImpl الملكية للسماح الفئات المشتقة لتوفير' بو 'الصحيح. يمكن أن أنواع المشتقة ثم تجاوز نوع عودة "الغائط" عن طريق إخفاء تنفيذ الفئة الأساسية.

وE.x:.

namepace ConsoleApplication8

{
public class Poo { }

public class RadioactivePoo : Poo { }

public interface Animal
{
    Poo Excrement { get; }
}

public class AnimalBase
{
    public Poo Excrement { get { return ExcrementImpl; } }

    protected virtual Poo ExcrementImpl
    {
        get { return new Poo(); }
    }
}

public class Dog : AnimalBase
{
    // No override, just return normal poo like normal animal
}

public class Cat : AnimalBase
{
    protected override Poo ExcrementImpl
    {
        get { return new RadioactivePoo(); }
    }

    public new RadioactivePoo Excrement { get { return (RadioactivePoo)ExcrementImpl; } }
}
}

وتصحيح لي إذا ايم خطأ ولكن ليست من بيت القصيد من pollymorphism لتكون قادرة على العودة RadioActivePoo إذا فإنه يرث من بو، العقد سيكون نفس فئة مجردة ولكن فقط العودة RadioActivePoo ()

وجرب هذا:

namespace ClassLibrary1
{
    public interface Animal
    {   
        Poo Excrement { get; }
    }

    public class Poo
    {
    }

    public class RadioactivePoo
    {
    }

    public class AnimalBase<T>
    {   
        public virtual T Excrement
        { 
            get { return default(T); } 
        }
    }


    public class Dog : AnimalBase<Poo>
    {  
        // No override, just return normal poo like normal animal
    }

    public class Cat : AnimalBase<RadioactivePoo>
    {  
        public override RadioactivePoo Excrement 
        {
            get { return new RadioactivePoo(); } 
        }
    }
}

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

وآمل أن يكون هذا آخر لا يزال من الممكن أن يساعد شخص ما، على الرغم من كونه 8 أشهر في وقت متأخر.

public interface Animal
{
    Poo Excrement { get; }
}

public class Poo
{
}

public class RadioActivePoo : Poo
{
}

public class AnimalBase : Animal
{
    public virtual Poo Excrement { get { return new Poo(); } }
}

public class Dog : AnimalBase
{
    // No override, just return normal poo like normal animal
}

public class CatBase : AnimalBase
{
    public override Poo Excrement { get { return new RadioActivePoo(); } }
}

public class Cat : CatBase
{
    public new RadioActivePoo Excrement { get { return (RadioActivePoo) base.Excrement; } }
}

ويمكن أن يساعد إذا مشتق RadioactivePoo من براز ومن ثم استخدام الأدوية.

ولمعلوماتك. ويتم تنفيذ هذا بسهولة تامة في سكالا.

trait Path

trait Resource
{
    def copyTo(p: Path): Resource
}
class File extends Resource
{
    override def copyTo(p: Path): File = new File
    override def toString = "File"
}
class Directory extends Resource
{
    override def copyTo(p: Path): Directory = new Directory
    override def toString = "Directory"
}

val test: Resource = new Directory()
test.copyTo(null)

وهنا مثال حي يمكن أن تقوم به مع: http://www.scalakata.com/50d0d6e7e4b0a825d655e832

وأعتقد يسمى إجابتك التغاير.

class Program
{
    public class Poo
    {
        public virtual string Name { get{ return "Poo"; } }
    }

    public class RadioactivePoo : Poo
    {
        public override string Name { get { return "RadioactivePoo"; } }
        public string DecayPeriod { get { return "Long time"; } }
    }

    public interface IAnimal<out T> where T : Poo
    {
        T Excrement { get; }
    }

    public class Animal<T>:IAnimal<T> where T : Poo 
    {
        public T Excrement { get { return _excrement ?? (_excrement = (T) Activator.CreateInstance(typeof (T), new object[] {})); } } 
        private T _excrement;
    }

    public class Dog : Animal<Poo>{}
    public class Cat : Animal<RadioactivePoo>{}

    static void Main(string[] args)
    {
        var dog = new Dog();
        var cat = new Cat();

        IAnimal<Poo> animal1 = dog;
        IAnimal<Poo> animal2 = cat;

        Poo dogPoo = dog.Excrement;
        //RadioactivePoo dogPoo2 = dog.Excrement; // Error, dog poo is not RadioactivePoo.

        Poo catPoo = cat.Excrement;
        RadioactivePoo catPoo2 = cat.Excrement;

        Poo animal1Poo = animal1.Excrement;
        Poo animal2Poo = animal2.Excrement;
        //RadioactivePoo animal2RadioactivePoo = animal2.Excrement; // Error, IAnimal<Poo> reference do not know better.


        Console.WriteLine("Dog poo name: {0}",dogPoo.Name);
        Console.WriteLine("Cat poo name: {0}, decay period: {1}" ,catPoo.Name, catPoo2.DecayPeriod);
        Console.WriteLine("Press any key");

        var key = Console.ReadKey();
    }
}

هل يمكن أن مجرد استخدام عودة واجهة. في قضيتك، IPoo.

وهذا هو الأفضل لاستخدام نوع عام، في قضيتك، لأنك تستخدم فئة أساسية للتعليق.

حسنا، فمن الممكن في الواقع لإرجاع نوع الخرسانة التي تختلف من نوع عودة الموروثة (حتى بالنسبة لأساليب ثابتة)، وذلك بفضل dynamic:

public abstract class DynamicBaseClass
{
    public static dynamic Get (int id) { throw new NotImplementedException(); }
}

public abstract class BaseClass : DynamicBaseClass
{
    public static new BaseClass Get (int id) { return new BaseClass(id); }
}

public abstract class DefinitiveClass : BaseClass
{
    public static new DefinitiveClass Get (int id) { return new DefinitiveClass(id);
}

public class Test
{
    public static void Main()
    {
        var testBase = BaseClass.Get(5);
        // No cast required, IntelliSense will even tell you
        // that var is of type DefinitiveClass
        var testDefinitive = DefinitiveClass.Get(10);
    }
}

وأنا نفذت هذا في المجمع API كتبت لشركتي. إذا كنت تخطط لتطوير API، وهذا له القدرة على تحسين قابلية الاستخدام وتجربة ديف في بعض الحالات استخدام. ومع ذلك، فإن استخدام dynamic له تأثير على الأداء، وذلك في محاولة لتجنب ذلك.

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