简体   繁体   English

为什么初始化派生类变量并将其分配给其基类类型不允许访问其成员?

[英]Why does initialising a derived class variable and assigning to its base class type not allow access to its members?

I have started learning C# and object oriented programming and I must admit it's really fun. 我已经开始学习C#和面向对象的编程,我必须承认这真的很有趣。 Now, as I was learning about inheritance and polymorphism, the following thought crossed my mind and I tried to make it work in code and see the results. 现在,当我学习继承和多态性时,我想到了以下想法,并试图使其在代码中起作用并查看结果。 I'll post the code and then ask specific questions so that it'll be easier for anyone answering to follow the train of thought that I had. 我将发布代码,然后提出具体问题,以便回答的每个人都可以更轻松地遵循我的思路。

class Program
{
    public static void Main(string[] args)
    {
        B b = new B();
        b.b = 5;
        A a = b;
        Console.WriteLine(a.b);
    }
}

class A
{
}

class B : A
{
    public int b;
}

Now, when I try and run the above code, it gives me a compile time error saying "'ConsoleApplication1.A' does not contain a definition for 'b' and no extension method 'b' accepting a first argument of type 'ConsoleApplication1.A' could be found". 现在,当我尝试运行上面的代码时,它给了我一个编译时错误,说“ ConsoleApplication1.A”不包含“ b”的定义,并且没有扩展方法“ b”接受“ ConsoleApplication1”类型的第一个参数。可以找到“。 Essentially what I understand this means is that the member variable b is not accessible. 从本质上讲,我的意思是成员变量b是不可访问的。

However, if I comment out the Console.WriteLine() and execute in Debug mode and hover over the a variable declared as 'A' class type, the object it points to does have a member variable b with the value of 5. 但是,如果我注释掉Console.WriteLine()并在Debug模式下执行并将鼠标悬停在声明为'A'类类型的变量上,则它指向的对象确实具有成员变量b,其值为5。

My question is - 我的问题是-

  • Why isn't the member variable accessible for the a variable of class type A? 为什么成员类型A的变量不能访问成员变量? (I'll admit I sort of understand that at compile time the compiler doesn't know that the object being assigned to the variable 'a' has the member variable b which is assigned the value 5, which brings me to my next question) (我承认我有点理解,在编译时,编译器不知道分配给变量“ a”的对象具有成员变量b,该成员变量b的赋值为5,这使我想到下一个问题)
  • Is this some sort of inherent flaw in the concept of inheritance that although objects of the base class have members of the derived class, they cannot be accessed because they are assigned to variables of base class type? 继承概念是否存在某种固有的缺陷,即尽管基类的对象具有派生类的成员,但由于将它们分配给基类类型的变量而无法访问它们?

Your self-answer in the first bullet point is correct: the compiler doesn't look at what values have been assigned to a variable when working out what members are available. 您在第一个要点上的自我回答是正确的:在确定可用的成员时,编译器不会查看为变量分配了哪些值。 It only cares about the compile-time type of the variable. 它只关心变量的编译时类型。 It's not about whether the b variable is "accessible" in the normal sense (public, private, internal etc) - it's about whether the member is even considered to exist as far as the compiler is concerned. 这与正常情况下(公共,私有,内部等) b变量是否“可访问”有关,而与就编译器而言该成员是否被认为存在有关。 You're using an expression of type A , so when the compiler tries to resolve member b , it simply doesn't find it. 您正在使用类型A的表达式,因此,当编译器尝试解析成员b ,它根本找不到它。

As for your second bullet point: no, I don't see that as an inherent flaw at all. 关于您的第二点要点:不,我完全不认为这是一个固有的缺陷。 Arguably it's a benefit , in that it makes you think about the level of abstraction you're trying to work at. 可以说这是一种好处 ,因为它使您可以考虑要使用的抽象级别。 That in turns allows you to replace one implementation of that abstraction with another. 依次地,您可以用另一种替换该抽象的一种实现。 If you could always (somehow) access the derived type's members, then if you later wanted to replace the value with an instance of a different derived type, your code could break. 如果您总是(以某种方式)访问派生类型的成员,那么如果您以后想用另一个派生类型的实例替换该值,则代码可能会中断。 If you want to depend on the value of a being a B reference, then simply declare the variable as being of type B rather than A . 如果要依赖a的值作为B引用,则只需将变量声明为B类型而不是A类型。 By declaring the variable using type A , you're explicitly saying "I don't care what the implementation is; I only care about using this object from the perspective of it being an A ." 通过使用类型A声明变量,您明确地说:“我不在乎实现是什么;我只关心从它作为A的角度使用此对象。”

There's one way round this in C# though: using dynamic instead. 但是,在C#中有一种解决方法:使用dynamic代替。 That way, all member access is resolved at execution time instead of compile time, and acts on the execution-time type of the dynamically-typed variable. 这样,所有成员访问都在执行时而不是编译时解决,并且对动态类型变量的执行时类型起作用。 (Or rather, whatever view of that you would normally have access to, following the normal access control rules.) (或者更确切地说,遵循正常的访问控制规则,您通常可以访问的任何视图。)

For example: 例如:

B b = new B();
b.b = 5;
dynamic a = b;
Console.WriteLine(a.b);

That will compile and run. 那将编译并运行。 Of course, it's not very safe, because you could equally have: 当然,它不是很安全,因为您同样可以拥有:

Console.WriteLine(a.typoInMemberName);

... which will still compile, but fail at execution time. ...仍将编译,但在执行时失败。 This is one downside of using dynamic typing. 这是使用动态类型的缺点。

You can see the variable by casting a to B. (a as B).b You cannot access members that are not declared. 您可以通过将a强制转换为B来查看变量。 (a as B).b您不能访问未声明的成员。 Only if class is dynamic like ViewBag. 仅当类是动态的(如ViewBag)时。 It is so because of strict typing in C# and is not really about OOP. 之所以如此,是因为在C#中进行了严格的键入,而实际上与OOP无关。 In PHP's OOP the code you posted is valid. 在PHP的OOP中,您发布的代码有效。

It isn't accessible "in that context". “在那种情况下”无法访问。 The context being a type "A" context, so it has no field "b". 该上下文是类型“ A”上下文,因此它没有字段“ b”。

Cast it to a "B" and you can access public fields. 将其强制转换为“ B”,即可访问公共字段。

 A a = b;
 Console.WriteLine((a as B).b); 

In this case, it isn't about accessibility (member access), it is about type signature. 在这种情况下,它与可访问性(成员访问)无关,而与类型签名有关。 What is an "A"? 什么是“ A”? An "A" doesn't have a .b field. “ A”没有.b字段。 however, if you use reflection to examine the "a" object, you should find the field, if it is really of type "B". 但是,如果使用反射检查“ a”对象,则应该找到该字段,如果它的类型确实是“ B”。

It isn't a flaw. 这不是缺陷。 Consider an alternative. 考虑替代方案。 What if "A" did have a "b" field, but you redefined the "b" field in class "B" (shadowing). 如果“ A”确实有一个“ b”字段,但是您在类“ B”中重新定义了“ b”字段(阴影),该怎么办。 What would you expect the compiler to do? 您希望编译器做什么? Which .b is correct? 哪个.b是正确的? The one dictated by the type context. 一个由类型上下文决定的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么将派生类的实例分配给类型不是实例而是其基类的变量 - Why to assign an instance of a derived class to a variable which Type is not the one of the instance, but its base class 为什么派生类不能用从原始类型派生的类型覆盖其基类中的属性? - Why can't a deriving class override a property in its base class with a type derived from the original type? 将派生类的实例分配给基类的变量 - Assigning instance of a derived class to a variable of base class 在其基类列表中查找派生类 - Find a derived class in a list of its base class 无法将派生类强制转换为其基类型 - Can't cast a derived class to its base type 访问派生类中的基类变量 - access base class variable in derived class 为什么基类对象无法在泛型基类约束中提供其成员函数和成员 - Why the base class object is not able to provide its member functions and members in Generics base class constraints 如何在派生类中调用,访问基类的公共成员 - How to invoke, access public members of base class in derived class 在IDisposable模式中,基类应该允许派生类共享其已处置的标志吗? - In an IDisposable pattern should a base class allow derived classes to share its disposed flag? C#使用泛型将派生类分配给基类变量 - C# assigning a derived class to a base class variable with generics
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM