以下是我正在使用的代码示例:
BaseClass class1;
if (userControl.Key == 100)
{
class1 = new DerivedClass1();
//This does not work, but it seems like it should
class1.PropertyInDerivedClass1 = 7
//This does work, but why should I have to cast something that I just instantiated?
((DerivedClass1)class1).PropertyInDerivedClass1 = 7;
}
else
class1 = new DerivedClass2();
我能做些什么来更轻松地访问派生类中的属性和方法吗?
答案 0 :(得分:12)
第二个结果是为所有人提供娱乐。血液,诉讼和最终被监禁中的第一个。
当然,一个随机的人可能是一个剑吞噬者,但如果你不知道他们那么你就不能安全地让他们吃剑。除非通过Person
引用处理它们,否则编译器不会使类SwordSwallower
的实例吃剑。
答案 1 :(得分:3)
如果您希望使用派生类的变量,则需要使用具有该特定类型的引用来解决它们。
BaseClass obj1;
DerivedClass dc = new DerivedClass();
dc.DerivedPropertyToAccess = value;
obj1 = dc;
总而言之,示例代码中的错误是使用带有基类类型而不是派生类的引用来访问属性。
答案 2 :(得分:1)
您可以使用派生类型的临时变量:
BaseClass baseObj;
if (…)
{
var derivedObj = new DerivedClass1();
baseObj = derivedObj;
derivedObj.DerivedProperty1 = "foo";
}
else
{
// Rinse, lather, repeat
}
或者,在您的具体示例中,您可以使用对象初始化器:
BaseClass baseObj;
if (…)
{
baseObj = new DerivedClass1 {
DerivedProperty1 = "foo"
}
} // etc…
答案 3 :(得分:1)
自动完成不会填充派生类成员,因为class1是BaseClass类型的变量。它不知道您实际上是在创建和分配存储在对BaseClass对象的引用中的派生类型。
我建议为派生类型创建一个新的本地副本,将其分配给BaseClass类型的原始变量,但使用本地副本进行派生的函数调用。
BaseClass class1;
if (userControl.Key == 100)
{
var derivedObject = new DerivedClass1();
class1 = derivedObject;
//This will now work
derivedObject.PropertyInDerivedClass1 = 7
}
else
class1 = new DerivedClass2();
答案 4 :(得分:1)
考虑这种情况:
// This decides what derived class to return depending on parameters you pass in
FunkyBase funky = FunkyFactory.Create("A");
如果编译器根据您传入的内容进行了一些分析以确定funky
总是FunkyDerivedA
,那么这两段代码紧密耦合。这段代码说“我知道这是FunkyBase
或者是从它派生的东西,,但这就是我所知道的,所以不要给我任何不在该基类上的选项” 。如果Visual Studio和.NET编译器在FunkyDerivedA
上为您提供了所有方法和属性,那么您可以这样做:
public class FunkyDerivedA : FunkyBase
{
public SomeProperty { get; set; }
}
///// SNIP /////
FunkyBase funky = FunkyFactory.Create("A");
funky.SomeProperty = 7;
然后一切正常,因为那是你正在使用的实际对象。但是,有一天会有一些变化,你想切换到FunkyDerivedB
,忘记该类在该类上不存在。
public class FunkyDerivedA : FunkyBase
{
public SomeProperty { get; set; }
}
// notice it doesn't have the same property
public class FunkyDerivedB : FunkyBase
{
}
///// SNIP /////
// Danger, Will Robinson!
FunkyBase funky = FunkyFactory.Create("B");
funky.SomeProperty = 7;
在那时,事情会以非常明显的方式失败。铸造是你知道你正在做什么的信号。它提醒您或维护您的代码的任何人,您正在假设您正在获取的对象类型(在本例中来自工厂方法),并且在更改此代码时应小心。
现在,这并不意味着C#不能做你所要求的。它可以在某种程度上。
从.NET v3.0开始,var
关键字(请参阅the MSDN article)允许您放弃声明类型:
var funky = new FunkyDerivedA();
智能感知和编译时类型检查工作,它只是根据方法的返回类型确定类型是什么。请注意,在我上面的工厂示例中,如果Create
方法只返回基类,那么这将是类型。它不会根据对调用树的分析或类似的东西将它强制转换为更加派生的类。
从.NET v4.0开始,您可以使用dynamic
(请参阅the MSDN article)关键字,该关键字放弃编译时类型检查,以便让您按照自己的意愿行事。当然,如果你弄错了,你会得到一个运行时异常(不是编译时错误),因为它是在运行时解决的,而不是在编译时解决的。同样,Intellisense不起作用,因此您需要确保知道对象是什么以及可用的成员。
另请注意,与投射一样,dynamic
关键字也表示您知道自己在做什么,并对任何不正确的事情负责。
希望有所帮助