【问题标题】:Why doesn't Visual Studio's intellisense work for derived classes?为什么 Visual Studio 的智能感知不适用于派生类?
【发布时间】:2012-02-13 09:56:57
【问题描述】:

这是我正在使用的代码示例:

    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();

我可以做些什么来更轻松地访问派生类中的属性和方法?

【问题讨论】:

  • 根据@DaveShaw 为C#保留关键字msdn.microsoft.com/en-us/library/x53a06bb.aspx的回复
  • 是的,你可以。您可以使用派生类类型的变量。
  • 您所要求的将需要某种数据流分析,以将转换插入编译器可以确定的是分配给变量的当前值,并使语言更加复杂。当您可以在 if 块中拥有一个派生类型的局部变量时,这听起来像任何语言设计人员都不会付出的努力。
  • 这不是 Intellisense/IDE 问题;这是一个关于静态类型和运行时多态的交互的问题。

标签: c# .net visual-studio oop inheritance


【解决方案1】:

如果您希望使用派生类的变量,您需要使用具有该特定类型的引用来处理它们。

BaseClass obj1; 
DerivedClass dc = new DerivedClass(); 
dc.DerivedPropertyToAccess = value; 
obj1 = dc;

总而言之,您的示例代码中的错误是使用基类类型而不是派生类的引用来访问属性。

【讨论】:

    【解决方案2】:

    它不起作用的原因是您试图访问BaseClass 上的DerivedClass1 的属性,但它们在该类中不存在。仅仅因为存储在变量class1 的对象是包含属性的类型,并不意味着您可以直接访问这些属性。以这种方式访问​​它们的唯一方法是指定class1DerivedClass1。所以要么你必须施放它,要么做类似的事情:

    BaseClass class1;
    if (userControl.Key == 100)
    {
        DerivedClass1 dClass1 = new DerivedClass1();
    
        // This will now work
        dClass1.PropertyInDerivedClass1 = 7;
    
        class1 = dClass1;
    }
    else
        class1 = new DerivedClass2();
    

    【讨论】:

      【解决方案3】:

      您可以只使用派生类型的临时变量:

      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…
      

      【讨论】:

        【解决方案4】:

        C# 智能感知使用与编译器相同的*静态分析

        你的代码行:

        class1.PropertyInDerivedClass1 = 7
        

        不给你智能,因为它不起作用。这不是有效的 C#,因为 class1 的编译时类型是 BaseClass 并且它没有该属性。

        【讨论】:

          【解决方案5】:

          自动完成不会填充派生类成员,因为 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();
          

          【讨论】:

            【解决方案6】:
            1. 随机选择一个人。让他们吃剑。
            2. 随机选择一个吞剑者。让他们吃剑。

            第二个结果是为所有人提供娱乐。流血、诉讼和最终入狱的第一次。

            当然,一个随机的人可能是吞剑者,但如果你不知道他们是,那么你就不能安全地让他们吃剑。编译器不会让 Person 类的实例吃剑,除非它通过 SwordSwallower 引用处理它们。

            【讨论】:

            • @Rotem 我不确定在当前经济环境下初级开发人员与初级剑客的比较成本。 ;)
            【解决方案7】:

            考虑这种情况:

            // 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 关键字也是一个信号,表明您知道自己在做什么,并对任何不正确的事情负责。

            希望有帮助

            【讨论】:

              猜你喜欢
              • 2015-07-02
              • 2022-12-03
              • 1970-01-01
              • 2016-09-30
              • 1970-01-01
              • 1970-01-01
              • 2011-08-07
              • 2022-07-19
              • 2010-10-17
              相关资源
              最近更新 更多