C#:压倒一切的回报类型
-
20-08-2019 - |
题
是没办法复盖的回报类型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
.你是什么意思,"甚至更强烈的归类"?
其他提示
我知道有很多解决方案,这个问题已经是但我认为我已经想出了一个修正的问题,我不得不与现有的解决方案。
我是不是快乐的一些现有的解决方案,原因如下:
- 保罗,第一个解决方案: 猫和狗没有一个共同的基类。
- 保罗,第二方案: 是有点复杂和难以阅读。
- 丹尼尔Daranas的解决方案: 这一工作,但它会扰乱你的代码有很多不必要的铸造和调试。Assert()发言。
- 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然后你会需要用它在你的decleration的"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从大便衍生,然后使用泛型。
FYI。这在实现的Scala很容易。
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
的使用具有的性能产生影响,因此要尽量避免。