考虑以下课程:
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>
这样的通用类并不起作用。
答案 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
属性确实值得。
让我们考虑一下情景:
Machine
类型的引用。好的,我无法真正利用强类型属性开始,machine.Cog
将返回Cog
类型的cog,这就是我所能要求的。BigMachine
类型的引用。好的,现在bigMachine
会返回Cog
类型的cog但我知道它真的是BigCog
。如果我真的需要,我可以简单地做一个演员:bigCog = (BigCog)(bigMachine.Cog)
。或者更好的是,只需实现仅属于BigMachine
的强类型属性:BigMachine.BigCog
。另外值得注意的是,返回类型差异根本不提供类型安全性。始终需要进行运行时类型检查,以确保没有人通过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; }
}
}
虽然我不喜欢那里演员。