你能在C#中同时影子和覆盖吗?

时间:2017-07-03 12:30:08

标签: c# override shadowing

考虑以下课程:

abstract class Cog {}
class BigCog: Cog {}
class SmallCog: Cog {}

abstract class Machine {}
class BigMachine: Machine {}
class SmallMachine: Machine {}

现在所有机器都使用cog,但BigMachine仅适用于BigCog,而SmallMachine仅适用于SmallCog。我想把它打造得很好,所以我可以这样做:

Machine machine;
BigMachine bigMachine;
SmallMachine smallMachine;

Cog cog;
BigCog bigCog;
SmallCog smallCog;

bigMachine.cog = bigCog; // OK
smallMachine.cog = smallCog; // OK

bigMachine.cog = smallCog; // Compiler error, type mismatch
smallMachine.cog = bigCog; // Compiler error, type mismatch

machine.cog = cog; // Runtime error if type mismatch
machine.cog = bigCog; // Runtime error if type mismatch
machine.cog = smallCog; // Runtime error if type mismatch

bigMachine.cog.BigCogMethod(); // OK, BigMachine.cog is of type BigCog
smallMachine.cog.SmallCogMethod(); // OK, SmallMachine.cog is of type SmallCog

我能想到实现这个目标的唯一方法是,我可以覆盖AND遮蔽cog属性:

abstract class Machine
{
    public virtual Cog cog { get; set; }
}
class BigMachine
{
    public override Cog cog
    {
        get
        {
            return base.cog;
        }
        set
        {
            if ( value != null && !(value is BigCog) )
                throw new ArgumentException();
            base.cog = value;
        }
    }
    new public BigCog cog // Compiler error, member "cog" already declared
    {
        get { return (BigCog)base.cog; }
        set { base.cog = value; }
    }
}

不幸的是编译器抱怨已经有一个名为cog的类成员,所以我不能同时覆盖它和阴影。还有其他一些优雅的模式可以解决这个难题吗?请注意,我确实需要对抽象Machine类型变量采取行动并执行machine.cog = cog;事情,因此制作像class BigMachine: Machine<BigCog>这样的通用类并不起作用。

7 个答案:

答案 0 :(得分:2)

怎么样:

abstract class Machine<T> where T:Cog
{
    public virtual T cog { get; set; }
}

class BigMachine : Machine<BigCog>
{

}

您可以在类型参数上创建带Constraint的通用基类。这样,您将cog属性的类型仅限于从Cog类型派生的类型。

答案 1 :(得分:2)

如果我理解你是正确的,那么当cog类型不匹配时你想要RunTime错误?如何使用interface + Explicit实现?

interface IMachine
{
    Cog cog { get; set; }
}

abstract class Machine : IMachine
{
    Cog IMachine.cog { get; set; }
}

class BigMachine : Machine, IMachine
{
    public BigCog cog { get; set; }

    Cog IMachine.cog
    {
        get { return cog; }
        set { cog = (BigCog) value; }
    }
}

class SmallMachine : Machine, IMachine
{
    public SmallCog cog { get; set; }
    Cog IMachine.cog
    {
        get { return cog; }
        set { cog = (SmallCog)value; }
    }
}

而不是Machine类使用IMachine接口

List<IMachine> list = new List<IMachine>();
list.Add(new BigMachine{cog =  new BigCog()});
list.Add(new SmallMachine{ cog= new SmallCog()});
list[1].cog = new BigCog(); // runtime error. Can not convert BigCog to SmallCog

答案 2 :(得分:1)

C#不支持返回类型差异,这使整个情况变得一团糟。

我退后一步,仔细考虑是否BigMachine返回BigCog输入的Cog属性确实值得。

让我们考虑一下情景:

  1. 我有一个Machine类型的引用。好的,我无法真正利用强类型属性开始,machine.Cog将返回Cog类型的cog,这就是我所能要求的。
  2. 我有一个BigMachine类型的引用。好的,现在bigMachine会返回Cog类型的cog但我知道它真的是BigCog。如果我真的需要,我可以简单地做一个演员:bigCog = (BigCog)(bigMachine.Cog)。或者更好的是,只需实现仅属于BigMachine的强类型属性:BigMachine.BigCog
  3. 另外值得注意的是,返回类型差异根本不提供类型安全性。始终需要进行运行时类型检查,以确保没有人通过Cog类型的引用添加错误键入的Machine。这种类型的设置总是在运行时爆炸,你不能静态地使它安全(没有泛型)。

    所以,所有这些混乱只会在一个非常具体的场景中为你节省一个演员阵容(大多数Machine个引用可能会被Machine输入开始。)

    有一个原因是返回类型方差不在语言中,因为它虽然方便,但它并不是一个非常棒的功能。没有它就可以生活得很好。因此,如果类型系统根本没有帮助,不要打它,你可能会过度复杂化。

答案 3 :(得分:0)

哦,另一种方法,我认为甚至比the previous更差。

abstract class Machine
{
    public virtual Cog cog { get; set; }
}
abstract class BigMachineBase: Machine
{
    public override Cog cog
    {
        get { return base.cog; }
        set
        {
            if ( value != null && !(value is BigCog) )
                throw new ArgumentException();
            base.cog = value;
        }
    }
}

class BigMachine: BigMachineBase
{
    new public BigCog cog
    {
        get { return (BigCog)base.cog; }
        set { base.cog = cog; }
    }
}

答案 4 :(得分:0)

这就是我要提供的(也不是完美的,但我想这就是我的方式):

abstract class Cog { };
class BigCog : Cog { }
class SmallCog : Cog { }

abstract class Machine
{
    public Cog Cog
    {
        get { return GetCog(); }
        set { SetCog(value); }
    }
    protected abstract Cog GetCog();
    protected abstract void SetCog(Cog value);
}
abstract class TypedMachine<T> : Machine where T: Cog
{
    private T typeSafeCog;
    public new T Cog
    {
        get { return typeSafeCog; }
        set { typeSafeCog = value; }
    }
    protected override Cog GetCog()
    {
        return typeSafeCog;
    }
    protected override void SetCog(Cog value)
    {
        if (value is T)
            typeSafeCog = (T)value;
        else
            throw new Exception("type error!");
    }
}
class BigMachine : TypedMachine<BigCog> { }
class SmallMachine : TypedMachine<SmallCog> { }

class Program
{
    static void Main(string[] args)
    {
        var bigCog = new BigCog();
        var smallCog = new SmallCog();
        var bigMachine = new BigMachine();
        bigMachine.Cog = smallCog; // compile error
        bigMachine.Cog = bigCog; // ok
        Machine[] anyMachine = { new SmallMachine(), new BigMachine() };
        anyMachine[0].Cog = smallCog; // ok
        anyMachine[1].Cog = smallCog; // runtime exception
        anyMachine[1].Cog = bigCog; // ok
        Console.ReadKey();
    }
}

答案 5 :(得分:-1)

嗯,这是我提出的一种方法,虽然我对它不是很满意,所以我会看看是否有更好的方法出现:

abstract class Machine
{
    protected virtual Cog _cog { get; set; }
    public Cog cog
    {
        get { return this._cog; }
        set { this._cog = value; }
    }
}

class BigMachine: Machine
{
    protected override Cog _cog
    {
        get { return base._cog; }
        set
        {
            if ( value != null && !(value is BigCog) )
                throw new ArgumentException();
            base._cog = value;
        }
    }
    new public BigCog cog
    {
        get { return (BigCog)base._cog; }
        set { base._cog = value; }
    }
}

答案 6 :(得分:-2)

假设您不必使用继承,这将是另一种方法:

public class BigMachine
{
    private readonly Machine _machine;

    public BigMachine(Machine machine)
    {
        _machine = machine;
    }

    public BigCog Cog
    {
        get { return (BigCog)_machine.cog; }
        set { _machine.cog = value; }
    }
}

虽然我不喜欢那里演员。