Почему IntelliSense Visual Studio не работает для производных классов? - PullRequest
3 голосов
/ 18 января 2012

Вот пример кода, с которым я работаю:

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

Есть ли что-нибудь, что я могу сделать, чтобы иметь более легкий доступ к свойствам и методам в производных классах?

Ответы [ 7 ]

12 голосов
/ 18 января 2012
  1. Выберите случайного человека. Заставь их есть меч.
  2. Выберите случайного глотателя мечей. Заставь их есть меч.

Второй результат - развлечение для всех. Первое в крови, судебные процессы и возможное заключение в тюрьму.

Теперь, конечно, случайный человек может быть глотателем меча, но если вы не знаете, что это так, вы не сможете безопасно заставить его съесть меч. Компилятор не будет делать экземпляры класса Person есть мечи, если он не обрабатывает их с помощью SwordSwallower ссылок.

3 голосов
/ 18 января 2012

Если вы хотите использовать переменные производного класса, вам нужно обратиться к ним, используя ссылку с этим конкретным типом.

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

Подводя итог, ошибка в вашем примере кода заключалась в доступе к свойствамиспользование ссылки с типом базового класса вместо производного класса.

2 голосов
/ 18 января 2012

C # intellisense использует тот же * статический анализ, что и компилятор

ваша строка кода:

class1.PropertyInDerivedClass1 = 7

Не дает вам смысла, потому что он не работает. Это недопустимо в C #, потому что тип времени компиляции class1 BaseClass и у него нет этого свойства.

2 голосов
/ 18 января 2012

Причина, по которой это не работает, заключается в том, что вы пытаетесь получить доступ к свойствам DerivedClass1 на BaseClass, но они не существуют в этом классе. Тот факт, что объект, хранящийся в переменной class1 , относится к типу, содержащему свойства, , но это не означает, что вы можете напрямую обращаться к этим свойствам. Единственный способ получить к ним доступ таким образом - указать, что class1 равно DerivedClass1. Так что либо вы должны разыграть его, либо сделать что-то вроде:

BaseClass class1;
if (userControl.Key == 100)
{
    DerivedClass1 dClass1 = new DerivedClass1();

    // This will now work
    dClass1.PropertyInDerivedClass1 = 7;

    class1 = dClass1;
}
else
    class1 = new DerivedClass2();
1 голос
/ 18 января 2012

Рассмотрим этот сценарий:

// 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 (см. статью MSDN ) позволяет отказаться от объявления типа:

var funky = new FunkyDerivedA();

Проверка типов Intellisense и времени компиляции, она просто выясняет, какой тип основан на типе возвращаемого значения метода.Обратите внимание, что в приведенном выше примере с фабрикой, если метод Create просто возвращает базовый класс, это будет тип.Он не приведёт его к более производному классу, основанному на анализе дерева вызовов или чего-либо подобного.

В .NET v4.0 вы можете использовать dynamic (см. MSDNКлючевое слово article ), которое прекращает проверку типов во время компиляции, позволяя вам делать то, что вы хотите.Конечно, если вы ошибетесь, вы получите исключение времени выполнения (не ошибка времени компиляции), поскольку оно разрешается во время выполнения, а не во время компиляции.Точно так же Intellisense не работает, поэтому вам необходимо убедиться, что вы знаете, что это за объект и какие члены доступны.

Также обратите внимание, что, как и при кастинге, ключевое слово dynamic также является сигналом того, что вы знаете, что делаете, и несете ответственность за все, что не правильно.

Надеюсь, это поможет

1 голос
/ 18 января 2012

Автозаполнение не будет заполняться производными членами класса, поскольку 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();
1 голос
/ 18 января 2012

Вы можете просто использовать временную переменную производного типа:

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…
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...