C #: переопределение типов возврата

Есть ли способ переопределить возвращаемые типы в C #? Если да, то как, а если нет, то почему и как это рекомендуется делать?

В моем случае у меня есть интерфейс с абстрактным базовым классом и его потомками. Я хотел бы сделать это (хорошо, не совсем, но в качестве примера!):

<code>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(); } }
}
</code>

RadioactivePoo конечно наследует отPoo.

Моя причина хотеть это так, чтобы те, кто используетCat объекты могут использоватьExcrement собственность без необходимости бросатьPoo вRadioactivePoo в то время как, например,Cat все еще может быть частьюAnimal список, в котором пользователи могут не знать или заботиться о своем радиоактивном пуле. Надеюсь, что это имеет смысл ...

Насколько я вижу, компилятор, по крайней мере, не допускает этого. Так что я думаю, что это невозможно. Но что бы вы порекомендовали в качестве решения этой проблемы?

 Paolo Tedesco26 июн. 2009 г., 14:39
Я тоже хочу сказать спасибо за пример: @goodgai ниже предлагает создать абстрактный Poo - интересно, как я мог бы объяснить это непрограммисту ...
 Gurgadurgen28 июн. 2014 г., 17:22
Я почти разочарован в себе тем, как забавно я нашел эти примеры XD
 Svish29 июн. 2009 г., 10:50
@Konrad: Слегка не уверен, стоит ли мнеjust смейтесь над этим блестящим комментарием, или я должен также спросить, действительно ли это ужасный запах кода, на который следует указать (потому что в этом случае я хотел бы знать об этом: p)
 Arnis Lapsa26 июн. 2009 г., 14:31
Как насчет дженериков? Разве они не помогут?
 hometoast26 июн. 2009 г., 14:35
Разве Cat.Excrement () не должен просто возвращать экземпляр RadioActivePoo как Poo? У вас общий интерфейс, используйте его. (И спасибо за истеричный пример.)

Ответы на вопрос(13)

IPoo.

Это предпочтительнее, чем использование универсального типа, в вашем случае, потому что вы используете базовый класс комментариев.

если RadioactivePoo является производным от poo, а затем использовать непатентованные значения.

если я ошибаюсь, но не весь смысл поллиморфизма в возможности вернуть RadioActivePoo, если он наследуется от Poo, контракт будет таким же, как абстрактный класс, но просто вернет RadioActivePoo ()

 26 июн. 2009 г., 14:40
Вы совершенно правы, но он хочет избежать дополнительного броска и некоторой более сильной типизации, для чего в основном и используются дженерики ...

что я нашел способ, который зависит не от обобщений или методов расширения, а от сокрытия методов. Однако он может нарушить полиморфизм, поэтому будьте особенно осторожны, если вы продолжите наследовать от Cat.

Я надеюсь, что этот пост еще может кому-нибудь помочь, несмотря на опоздание на 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; } }
}
 26 мая 2011 г., 09:35
ваше & quot; решение & quot;DOES сломать полиморфизм, так что это не совсем решение. С другой стороны, решение hjb - реальное решение, и довольно ИМХО.
 20 февр. 2010 г., 03:46
Aack, не бери в голову. Я не догадывался, что hjb417 уже опубликовал подобное решение. По крайней мере, мой не требует модификации базового класса.

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(); } 
        }
    }
}
 05 дек. 2012 г., 05:43
Какой смысл интерфейса Animal здесь? Ничто не наследует от этого.

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

return type covariance и не поддерживается в C # или .NET в целом, несмотря на то, что некоторые людипожелания.

То, что я хотел бы сделать, это сохранить ту же подпись, но добавить дополнительнуюENSURE предложение к производному классу, в котором я гарантирую, что этот возвращаетRadioActivePoo, Итак, короче говоря, я делаю с помощью контракта дизайн, что я не могу делать с помощью синтаксиса.

Другие предпочитаютне настоящие это вместо. Это нормально, я думаю, но я склонен экономить «инфраструктуру» строки кода. Если семантика кода достаточно ясна, я счастлив, и проектирование по контракту позволяет мне достичь этого, хотя это не механизм времени компиляции.

То же самое для дженериков, которыедругие ответы предложить. Я бы использовал их по лучшей причине, чем просто возвращение радиоактивного какашка - но это только я.

 26 июн. 2009 г., 14:56
В .Net и перед тем, как увидеть контракты по коду .Net 4.0, я пишу предложения ENSURE (x) просто как «Debug.Assert (x)». Для дальнейших ссылок см. Напримерarchive.eiffel.com/doc/manuals/technology/contract/page.html или Построение объектно-ориентированного программного обеспечения, 2-е издание, Бертрана Мейера (1994), глава 11.
 Svish26 июн. 2009 г., 14:53
Что такое пункт ENSURE? Как это будет работать? Это атрибут в .Net?
 20 февр. 2010 г., 12:06
"Я хотел бы использовать их по лучшей причине, чем просто возвращение радиоактивного какашка - но это только я" принадлежит в мой список любимых цитат :)

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

Вы теряете возможность использовать базовый класс для реализации Cat, но, с другой стороны, вы сохраняете полиморфизм между Cat и Dog.

Но я сомневаюсь, что дополнительная сложность того стоит.

который создает «экскременты»? и сохранить государственную собственность, которая возвращает «экскременты»; не виртуальный. Затем производные классы могут переопределять тип возвращаемого значения базового класса.

В следующем примере я делаю «экскременты»; не виртуальные, но предоставляют свойство ExcrementImpl, чтобы позволить производным классам обеспечивать надлежащий «Poo». Производные типы могут затем переопределять тип возвращаемого значения «Excrement». скрывая реализацию базового класса.

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; } }
}
}
 17 февр. 2012 г., 08:55
пример сделал это намного труднее для понимания. но отличный код!

на самом деле можно вернуть конкретный тип, который отличается от унаследованного возвращаемого типа (даже для статических методов), благодаря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 оказывает влияние на производительность, поэтому постарайтесь избежать этого.

что уже есть много решений этой проблемы, но я думаю, что я нашел решение, которое устраняет проблемы, которые у меня были с существующими решениями.

I wasn't happy with the some of the existing solutions for the following reasons:

Paolo Tedesco's first solution: Cat and Dog do not have a common base class. Paolo Tedesco's second solution: It is a bit complicated and hard to read. Daniel Daranas's solution: This wor,ks but it would clutter up your code with a lot of unnecessary casting and Debug.Assert() statements. hjb417's solutions: This solution doesn't let you keep your logic in a base class. The logic is pretty trivial in this example (calling a constructor) but in a real world example it wouldn't be.

My Solution

Это решение должно преодолеть все проблемы, которые я упомянул выше, используя как дженерики, так и скрытие методов.

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> { }

С этим решением вам не нужно ничего переопределять в Dog или Cat! Как это круто? Вот пример использования:

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

Это выведет: & quot; RadioactivePoo & quot; дважды, что показывает, что полиморфизм не был нарушен.

Further Reading

Explicit Interface Implementation new Modifier. I didn't use it in this simplified solution but you may need it in a more complicated solution. For example if you wanted to create an interface for BaseAnimal then you would need to use it in your decleration of "PooType Excrement". out Generic Modifier (Covariance). Again I didn't use it in this solution but if you wanted to do something like return MyType<Poo> from IAnimal and return MyType<PooType> from BaseAnimal then you would need to use it to be able to cast between the two.
 19 нояб. 2017 г., 19:57
@AnestisKivranoglou да оба будут иметь тип RadioactivePoo
 18 июл. 2018 г., 02:52
Я пахал SO в течение 48 часов, и этот ответ только что сломал его. И я был как ... "Чувак, это может быть очень круто."
 18 нояб. 2017 г., 12:48
Относительно безопасности типов будут оба типа bruce.Excement и bruceAsAnimal.Excrement типа RadioactivePoo ??
 08 мая 2013 г., 17:42
Я думаю, что нашел решение для связанной проблемы, когда метод должен возвращать унаследованный тип, то есть метод в «Dog». который был унаследован от «Animal» тем не менее возвращает Dog (это), а не Animal. Это сделано с помощью методов расширения, я мог бы поделиться здесь, когда я доберусь до него.
 08 мая 2013 г., 17:40
Чувак, это может быть супер круто. У меня нет времени для дальнейшего анализа в данный момент, но похоже, что вы, возможно, получили его, и если вы все-таки взломали это, высокие пять и спасибо за обмен. К сожалению, это «poo» и "экскременты"; бизнес это большое отвлечение.
Решение Вопроса

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> { }

EDIT: Новое решение, использующее методы расширения и интерфейс маркера ...

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();
    }
}

Таким образом, Dog и Cat наследуются от Animal (как отмечалось в комментариях, мое первое решение не сохранило наследство).
Необходимо явно пометить классы интерфейсом маркера, что является болезненным, но, возможно, это может дать вам некоторые идеи ...

SECOND EDIT @Svish: я изменил код, чтобы ясно показать, что метод расширения никоим образом не поддерживает тот факт, чтоiPooProvider наследует отBaseAnimal, Что вы подразумеваете под "еще более типизированным"?

 26 июн. 2009 г., 14:40
Просто думал о том же.
 26 июн. 2009 г., 14:51
Разве вы не потеряли поллиморфизм между собакой и кошкой?
 26 июн. 2009 г., 15:04
@Svish: я думаю, что как только это произойдет, пришло время использовать структуру внедрения зависимостей.
 Svish14 июл. 2009 г., 20:48
Как метод StronglyTypedExcrement узнает, что iPooProvider является BaseAnimal? Это просто угадать? Или это то, чего я не вижу? Возможно ли сделать этот метод еще более типизированным?
 Svish26 июн. 2009 г., 14:52
Что если бы были и другие типы, которые вы хотели бы переопределить? Технически вы можете получить целую кучу аргументов типа. Что, я думаю, могло бы меня раздражать ... но да, это решение.

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();
    }
}

Ваш ответ на вопрос