2012-03-16 2 views
1

Я, кажется, забыл некоторые из основных правил наследования, потому что я не могу понять, почему это не сработает. У меня есть класс SuffixNode, который расширяет узел.Простой наследование не работает?

Node:

class Node 
{ 
    public char label; 
    public Node parent; 
    public Dictionary<char,Node> children; 

    public Node(Node NewParent, char NewLabel) 
    { 
     this.parent = NewParent; 
     this.label = NewLabel; 
     children=new Dictionary<char,Node>(); 
    } 
} 

SuffixNode:

class SuffixNode: Node 
{ 
    public Dictionary<String, int> Location=new Dictionary<String, int>(); 

    public SuffixNode(Node NewParent):base(NewParent, '$') 
    { 

    } 

    public void AddLocation(String loc,int offset) 
    { 
     this.Location.Add(loc, offset); 
    } 
} 

Я пытаюсь вызвать метод AddLocation в основной программе из класса SuffixNode, но он дает мне ошибку о том, что нет такого метода существует (в классе узлов):

Node n; 
char FirstChar = suffix[0]; //first character of the suffix 
if (suffix == "") 
{ 
    return true; 
} 

//If the first character of a suffix IS NOT a child of the parent 
if (!parent.children.ContainsKey(FirstChar)) 
{ 
    if (FirstChar == '$') 
    { 
      n = new SuffixNode(parent); 
      n.AddLocation(document, offset); 
    } 
    else 
    { 
      n = new Node(parent, FirstChar); //Create a new node with the first char of the suffix as the label 
      parent.children.Add(FirstChar, n); //Add new node to the children collection of the parent 
    } 
}   

Я уверен, что это очень простой ответ, но я просто не могу понять, почему это не работает. Не должно быть

Node n = new SuffixNode(parent) 

Позволяет мне получить доступ к методам и переменным SuffixNode?

+0

Почему вы используете 'Node' вместо' SuffixNode' для 'n'? –

+0

Мое дерево будет содержать разные типы узлов, я думал, что это будет полезно позже, если я захочу использовать некоторые полиморфные методы. – Matt

+3

Отмечено, что 'if (suffix ==" ")' will * никогда не будет выполняться *, потому что, если условие было 'true', предыдущая строка (' char FirstChar = suffix [0]; ') выбросить исключение. – phoog

ответ

1

Многие ответы правильно утверждают, что вы не можете вызывать метод, определенный в производном классе, для переменной, тип которой является базовым классом. Ни один из них не отметил, что является основополагающим для безопасности типов, обеспечиваемых статической типизацией C#.

При вызове методы на переменном, что вызов методы решается во время компиляции, и нет никакого способа, компилятор может n.AddLocation когда n является Node типом переменным.

Альтернативой было бы разрешить вызов во время выполнения, что может привести к исключению, если референт n является экземпляром некоторого другого подкласса Node, что не имеет AddLocation метод (или на самом деле экземпляр Node). Система типов C# явно предназначена для предотвращения этой ситуации.

Рассмотрим:

object o1 = "Hello, World!"; 
Console.WriteLine(o1.Length); //hypothetically fine 

object o2 = Math.PI; 
Console.WriteLine(o2.Length); //exception! 

Философия C# отказобезо- быстро: уловы кодирования ошибок во время компиляции, когда это возможно, потому что ошибки поймана во время компиляции гораздо легче исправить, чем пойманный во время выполнения.

тривиальное решение, не изменяя модель объекта, было бы ввести новую переменную внутри соответствующего блока:

if (FirstChar == '$') 
{ 
    SuffixNode sn = new SuffixNode(parent); 
    sn.AddLocation(document, offset); 
    n = sn; 
} 
else 
{ 
    n = new Node(parent, FirstChar); //Create a new node with the first char of the suffix as the label 
    parent.children.Add(FirstChar, n); //Add new node to the children collection of the parent 
} 
+0

Спасибо за помощь! Принял этот ответ, потому что вы не только предоставляете решение, но и объясняете * почему * он не работал наилучшим образом. Ура! – Matt

1

Вам необходимо переместить метод AddLocation в базовый класс.

+0

Это решит проблему, но почему она не работает так, как есть? – Matt

+1

О, извините ... это потому, что вы вызываете Node.AddLocation, которого нет. Тип узла знает только о том, что он содержит.Но вы можете удалить Unode как SuffixNode и вызвать все методы, содержащиеся в узле. Просто не наоборот. –

+1

@Kentz, потому что метод является членом производного класса, но статическим типом переменной является базовый класс. Эта переменная может содержать объект (гипотетический) типа 'PrefixNode', который может не иметь метода AddLocation. – phoog

3
Node n=new SuffixNode(parent) 

Эта строка сообщает компилятору, что n имеет тип Node. Тот факт, что в настоящее время он присвоен SuffixNode, не имеет значения. Весь компилятор знает, что это Node, и поэтому вы можете только называть его Node.

Вероятно, самый простой способ, чтобы обойти это:

n = new SuffixNode(parent); 
((SuffixNode)n).AddLocation(document, offset); 

В основном это говорит компилятору, что вы на самом деле есть узел суффикс здесь.

Возможно, более понятно будет:

SuffixNode sn = new SuffixNode(parent); 
sn.AddLocation(document, offset); 
n = sn; 
5

Вы объявили тип п в Node, что на самом деле не имеет метод AddLocation. Вы должны объявить его как SuffixNode n = new SuffixNode(parent), чтобы иметь возможность вызвать функцию ребенка на нем, или добавить (возможно, абстрактный) метод в Node под названием AddLocation.

+1

Он не может объявить его как SuffixNode, потому что в другой ветке, если ему присвоен «Node». – Chris

+0

Хорошо, создание абстрактного метода имеет наибольший смысл в моем случае, спасибо! – Matt

+0

Это не имеет смысла. Абстрактные методы могут существовать только в абстрактных классах. Вы создаете конкретный экземпляр 'Node', поэтому вы не можете на него нарисовать абстрактный метод. Вам нужно будет либо создать новый абстрактный класс, либо создать новый класс, который может наследовать от «Node» и использоваться там, где вы сейчас создаете «Node's». Также мне кажется, что из вашего кода AddLocation имеет смысл только в классе суффиксов (в том, что он записывает только частную переменную только в этом классе). Вы хотите AddLocation на всех узлах? имеет ли смысл, чтобы объект имел это? – Chris

1

Не следует Node n = new SuffixNode(parent) Позволить мне получить доступ к методам и переменным SuffixNode?

Нет, тип переменной определяет, к каким методам у вас есть доступ.

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

В качестве альтернативы вы можете добавить его в базовый класс, если это имеет смысл.

+0

Хорошо, спасибо за объяснение :) – Matt

2

Если вы определяете виртуальный метод в узле и переопределяете его в SuffixNode, в вашем сценарии будет вызываться версия SuffixNode. Если, как и в вашем случае, метод не существует в узле, он не может быть вызван таким образом, потому что переменная n может содержать любой вид узла. Чтобы вызвать метод, вы должны сначала передать его в SuffixNode.

+0

Отличное объяснение, спасибо – Matt

1

Вы указали n как Node. Node не содержит определения для AddLocation, поэтому ваш код не будет компилироваться. Даже если вы знаете, что фактический, базовый тип - SuffixNode, компилятор должен придерживаться своих пушек и вызывать вас на ошибку.

Воображение такой сценарий:

class OtherNode : Node 
{ 

} 

Node n = new OtherNode(); 
n.AddLocation(...); 

Если это работает? Конечно, нет, но какое правило будет опускать этот код из компиляции и разрешить ваш пример? Как компилятор может знать, что вы не заменили базовый тип в более поздней точке программы? Это невозможно.

Вы идете не так; если этот метод должен быть доступен для всех потомков Node, тогда он должен быть хотя бы объявлен в Node (он может быть абстрактным, чтобы быть переоцененным в производных классах).

+0

Спасибо за расчистку :) – Matt

Смежные вопросы