0

Can someone explain what is the main difference between those two part of code, and why or when I should use a reference to a base class, as for me it is the same thing.

internal class MyBaseClass
{
    public string Field = "Hello";
    public void Print()
    {

        Console.WriteLine("This is the base class.");
    }
}

internal class MyDerivedClass : MyBaseClass
{
    public string FieldDerived = "Coucou";
    public new void Print()
    {

        Console.WriteLine("This is the derived class.");
    }
    public new void Print2()
    {

        Console.WriteLine("This is the derived class.");
    }
}

internal class Program
{
    private static void Main()
    {
        MyDerivedClass derived = new MyDerivedClass();
        MyBaseClass mybc = (MyBaseClass) derived;
                            // ↑
                            //   Cast to  base clas
    }
}

And this is the code, that for me do the exactly same thing :

internal class MyBaseClass
{
    public string Field = "Hello";
    public void Print()
    {

        Console.WriteLine("This is the base class.");
    }
}

internal class MyDerivedClass : MyBaseClass
{
    public string FieldDerived = "Coucou";
    public new void Print()
    {

        Console.WriteLine("This is the derived class.");
    }
    public new void Print2()
    {

        Console.WriteLine("This is the derived class.");
    }
}

internal class Program
{
    private static void Main()
    {
        MyDerivedClass derived = new MyDerivedClass();
        MyBaseClass mybc = new MyBaseClass();
                               // ↑
                               //   Use the base class 
    }
}
4

8 回答 8

4

用法有两点不同。

在第一个示例中,强制转换是不必要的,并且 mybc 和 derived 都指向同一个对象,它是派生类的一个实例。您始终可以将派生类视为基类的实例(因为它们是),不需要强制转换。

在第二个示例中,它们是完全独立的对象实例,并且引用的对象实际上也是独立的类型。

于 2013-04-29T13:47:24.787 回答
2

派生类用于实现 is-a 关系。想象一个类:

internal class MySpottedDogClass : MyDogClass : MyAnimalClass

因为斑点狗是狗,狗是动物

如果我创造一条新的斑点狗

MySpottedDogClass rover = new MySpottedDogClass();

然后我可以打电话

rover.countSpots() 

仅适用于斑点狗的动作。方法是 SpottedDog 类的一部分

rover.bark()

这适用于所有狗,因此在狗类中实现

rover.weight()

这适用于所有动物,因此在动物类中实现。子类是一种创建通用事物的更详细版本的方法。我认为您需要阅读一些有关面向对象设计的内容

也许从这些方面考虑您的设计会有所帮助。

你的第一个例子就像

SpottedDogClass rover = new SpottedDogClass();
DogClass dogReferenceToRover = (DogClass) rover;

它们都指向同一个对象。dogReferenceToRover.bark() 没问题。dogReferenceToRover.countSpots() 无效,因为您不能对狗(只有斑点狗)这样做,并且引用具有类型,而不是对象。

你的第二个例子是这样的

SpottedDogClass rover = new SpottedDogClass();
DogClass fido = new DogClass();

这次你做了一只斑点狗和一只狗。有2个对象。

于 2013-04-29T13:53:34.653 回答
2

不同之处在于您在第一个示例中只创建一个对象,而在第二个示例中创建两个对象。

  1. 创建对象并转换为其基类的对象。剥离 MyDerivedClass 添加的所有功能。投射前所做的所有更改都将保留。
  2. 创建一个 MyDerivedClass 的对象和一个 MyBaseClass 的对象,它们彼此无关。

 public void TestThisStuff()
        {
            // 1 example
            MyDerivedClass derivedObj = new MyDerivedClass();
            derivedObj.Field = "Changed field";
            derivedObj.FieldDerived = "Changed derived field";
            MyBaseClass baseObj = (MyBaseClass)derivedObj;
            // baseObj.Field value will be "Changed field"
            // baseObj.FieldDerived will not be accessible because it is not presend in the base class

// 2 example MyDerivedClass derivedObj2 = new MyDerivedClass(); derivedObj2.Field = "Changed field"; derivedObj2.FieldDerived = "Changed derived field"; MyBaseClass baseObj2 = new MyBaseClass(); // baseObj2.Field value will be "Hello" // baseObj2.FieldDerived will not be accessible because it is not presend in the base class baseObj2.Field = "Change my field because it's not related to anything else." // derivedObj2.Field value will be "Changed field" // derivedObj2.FieldDerived will not be accessible because it is not presend in the base class }
于 2013-04-29T13:47:26.157 回答
1

我不确定你想在“使用基类”中做什么。

通常,如果您有对派生类的引用,则不需要将其强制转换为它的基类。通常,当您“使用”派生类的抽象时,您会使用基类和派生类。也许你知道这一点,所以我没有任何不尊重的意思。例如,您的基类可以是 Animal,而您的派生类是 Dog 和 Cat。基类中的抽象可以是 Eat。因此,如果您有一个 Animals 列表,您可以通过调用抽象方法 Eat 来喂它们,而无需知道每个动物是哪种动物,也不必将每个动物强制转换为它的派生类。

对不起,如果不是你问的。

于 2013-04-29T13:52:22.647 回答
1

第一个示例中的强制转换是多余的,因为将派生类视为基类始终是合法的。

如果您想覆盖 Print 方法(这可能是您在这种情况下想要的),请将其标记为virtual基类和override派生类。然后您将在基类和派生类之间获得不同的行为。

于 2013-04-29T13:46:09.567 回答
1

如果你考虑一个人为的例子,

abstract class Cake
{
    public virtual bool Edible { get { return true; } }
}

class PooCake : Cake
{
    public new bool Edible { get { return false; } }
}

class TurdCake : Cake
{
    public override bool Edible { get { return false; } }
}

和下面的片段

var turd = new TurdCake();
var poo = new PooCake();
Console.WriteLine(
    "PooCake is{0} edible",
    poo.Edible ? string.Empty : " not");
Console.WriteLine(
    "PooCake is{0} edible",
    (Cake)poo.Edible ? string.Empty : " not");
Console.WriteLine(
    "TurdCake is{0} edible",
    turd.Edible ? string.Empty : " not");
Console.WriteLine(
    "TurdCake is{0} edible",
    (Cake)turd.Edible ? string.Empty : " not");
Console.ReadKey();

你会意识到你应该new非常小心地使用关键字,因为它破坏了继承模型。在这种情况下,它可能会让你吃掉基础便便蛋糕。

如果您调试并单步执行代码,您会看到,在TurdCake属性被正确覆盖并且基本实现不可访问的情况下。在PooCake示例中,该属性仅被隐藏,对基本类型的引用公开了基本实现。这可能会导致不满意的结果。

于 2013-04-29T14:21:35.377 回答
0

有时,由于许多不同的原因,派生类会隐藏基类中定义的方法或属性。

考虑这个例子:

public class BaseClass
{
    public string GetSomeValue() { return "base class"; }
}

在派生类中,这可以通过一种新方法“重新布线”:

public class DerivedClass : BaseClass
{
    // hides the base method (basically you're hijacking the signature)
    public new string GetSomeValue() { return "derived class"; }
}

请注意,您几乎可以new在基类上进行任何签名。这是一种非常强大的劫持签名的方法,应谨慎使用。

多态地,一个实例DerivedClass总是"derived class"GetSomeValue被调用时返回。

但是,有时调用基类方法而不是派生类方法可能非常重要。在这种情况下,像这样向上转换派生实例是有意义的:

var result = ((BaseClass)derivedInstance).GetSomeValue();  // returns "base class"

当您向上转换 ( (BaseClass)derivedInstance) 时,您可以使派生类忽略它自己的new方法并调用基类方法。这在某些情况下可能是有意义的,但除非您正在进行一些组件设计或其他多态繁重的代码提升,否则我认为您不会在日常工作中看到这一点。好吧,至少,没有(遮住眼睛)。

于 2013-04-29T14:46:52.487 回答
0

也许您的问题需要关于 c# 中类的多态行为?

于 2013-04-29T13:57:40.800 回答