2010-02-02 4 views
3

У меня возникают проблемы с получением желаемого поведения из этих нескольких классов и интерфейсов.Оператор перегрузки C# == и! =

Вот моя проблема,

//Inside a Unit Test that has access to internal methods and properties 

INode firstNode, secondNode; 

INodeId id = new NodeId (4); 

first = new Node (id, "node"); 
second = new Node (id, "node"); 

Assert.IsTrue (first == second); 

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

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

Вот часть Рамок я работаю:

public interface IIdentifier<T> where T : class 
{ 
    TKeyDataType GetKey<TKeyDataType>(); 

    bool Equals (IIdentifier<T> obj; 
} 

public interface INode 
{ 
    string name 
    { 
     get; 
    } 

    INodeId id 
    { 
     get; 
    } 
} 

public interface INodeId : IIdentifier<INode> 
{ 
} 

public class Node : INode 
{ 
    internal Node(INodeId id, string name) 
    { 
     //Work 
    } 

    public static bool operator == (Node n1, Node n2) 
    { 
     return n1.equals(n2); 
    } 

    public static bool operator != (Node n1, Node n2) 
    { 
     return !n1.equals(n2); 
    } 

    public bool Equals (INode node) 
    { 
     return this.name == node.name && 
       this.id = node.id; 
    } 

    #region INode Properties 

    } 

public class NodeId : INodeId 
{ 

    internal NodeId(int id) 
    { 
     //Work 
    } 

    public static bool operator == (NodeId n1, NodeId n2) 
    { 
     return n1.equals(n2); 
    } 

    public static bool operator != (NodeId n1, NodeId n2) 
    { 
     return !n1.equals(n2); 
    } 

    public override bool Equals (object obj) 
    { 
     return this.Equals ((IIdentifier<INode>) obj); 
    } 

    public bool Equals (IIdentifier<INode> obj) 
    { 
     return obj.GetKey<int>() == this.GetKey<int>(); 
    } 

    public TKeyDataType GetKey<TKeyDataType>() 
    { 
     return (TKeyDataType) Convert.ChangeType (
      m_id, 
      typeof (TKeyDataType), 
      CultureInfo.InvariantCulture); 
    } 


    private int m_id; 

} 
+0

Правильное равенство - довольно сложная задача. Вас могут заинтересовать эти примечания по некоторым соображениям дизайна по различным способам вычисления равенства: http://blogs.msdn.com/ericlippert/archive/2009/04/09/double-your-dispatch-double-your- fun.aspx –

+0

Как и в стороне, вы можете рассмотреть возможность переопределения метода GetHashCode(). В этом конкретном случае 'NodeId' просто вернет' m_id.GetHashCode() 'и' Node' будет вычислять комбинацию полей-членов. У Jon Skeet есть отличная реализация здесь: http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode/263416#263416 –

ответ

6

перегруженных оператора разрешаются во время компиляции на основе объявленных типов операндов, а не от фактического типа объектов во время выполнения. Альтернативный способ сказать это то, что перегрузки операторов не являются виртуальными. Таким образом, сравнение, которое вы делаете выше, это INode.operator==, а не Node.operator==. Поскольку INode.operator== не определен, перегрузка разрешается до Object.operator==, которая просто сравнивает ссылки.

Существует не очень хороший способ обойти это. Наиболее правильная вещь - использовать Equals(), а не == в любом месте, где операнды могут быть объектами. Если вам действительно нужна настоящая перегрузка виртуального оператора, вы должны определить operator == в корневом базовом классе, на который наследуются ваши объекты, и иметь этот вызов перегрузки Equals. Обратите внимание, однако, что это не будет работать для интерфейсов, что и есть у вас.

+0

Эй, я ценю помощь. Это, безусловно, поставит меня в правильном направлении. – Meiscooldude

+0

Обратите внимание, что по тем же причинам, изложенным в ответе, компилятор будет выбирать 'Object.Equals', а не' Node.Equals', даже если вы вызываете 'first.Equals (second)'. –

+0

@Fredrik: 'Equals' является виртуальным, тем не менее, поэтому вызванный метод будет самой высокоприбыльной реализацией' Equals' на фактическом типе времени выполнения. –

-1

.Equals может быть злом, если не сделано правильно.

Вот лучшие методы для осуществления этого:

http://msdn.microsoft.com/en-us/library/ms173147(VS.80).aspx

+4

.Equals - это " зло"? Почему это точно? Если бы ОП переопределил .Equals правильно, было бы хорошо, если не совсем точно, как я это сделаю. –

+0

Возможно, вы были заблокированы, потому что ваш ответ на самом деле не очень полезен и имеет мало общего с вопросом OP. –

+1

хе-хе, так что вы пошли и забрали три моих старых ответа из-за этого? Какой ребенок. –

1

Я думаю, вам может потребоваться переопределить Equals (object) в узле, как в NodeId. Итак:

public override bool Equals (object obj) 
{ 
    if (obj is Node) 
    { 
     return this.Equals(obj as Node); 
    } 
    return false; 
} 

// your code (modified to take a Node instead of an INode) 
public bool Equals (Node node) 
{ 
    return this.name == node.name && 
      this.id = node.id; 
} 
+0

Это неверно (поскольку узел не запечатан). Если класс наследуется от Узла, то рефлексивное свойство Equals() будет потеряно. Вам нужно проверить тип EXACT obj и это. – Bryan

+0

@Mark: вам не хватает ключевого слова 'override':' public override bool Equals (object obj) '. Кроме того, этот ответ решает проблему, о которой спрашивает ОП. –

+0

@Brian - Да, вы правы, я копирую и вставляю код под «// ваш код» и не понимаю, что он принимает INode, а не узел. Я обновлю сообщение. Благодаря! –

0

это с помощью == от объекта, поскольку firstNode и secondNode не типа Node, они типа INode. Компилятор не распознает базовые типы.

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

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