views:

121

answers:

5

With code like the following

public class Task
{
  string Name;
  public static bool operator ==(Task t1, Task t2)
  { return t1.Name = t2.Name && t1.GetType() == t2.GetType(); }
}
public class TaskA : Task
{
   int aThing;
   public static bool operator ==(TaskA t1, TaskA t2)
   { 
      return (Task)t1 == (Task)t2 && t1.GetType() == t2.GetType()
          && t1.aThing == t2.aThing; }
}
public class TaskB : Task  //more of the same

class Stuffin
{
   List<Task> Tasks;

   void CheckIt()
   {
      bool theSame = Tasks[0] == Tasks[1];
   }

I'm trying to make sure that the derived operator (TaskA.==) is called.

I get compilation error when trying the technique here.

I think i'd be able to get it to work correctly if the operator was not static, because i could then override the base classes operator. Is that possible?

Once i get that how would i compare the base properties (i would think the cast to task type [(Task)t1 == (Task)t2] would not work)?

+2  A: 

You can't. Operators aren't overridden, they're overloaded. That means the implementation to be used is entirely decided at compile-time.

One thing you can do is override Equals in Task and call it from ==, and then override it again in TaskA. That also makes the "base properties" check easy - just call base.Equals from TaskA.Equals.

Jon Skeet
+1  A: 

That post was about C++. Consider operator as static function. You can't overload it.

But, you can declare instance virtual method, and call it inside your operator. But note operator accepts two arguments, and they may have different actual type, so you need to deside how to prefer one argument from these two.

public class Task
{
     public virtual bool MyMethod(Task anotherTask) { return true; }

     public static bool operator==(Task t1, Task t2) 
     {
          return t1 == null ? false : t1.MyMethod(t2);
     } 
}

public class TaskA
{
      public override bool MyMethod(Task anotherTask) { return false; }
}
STO
+2  A: 

What you're trying to do is really quite difficult in C#. Basically what you want is an operator whose behaviour is determined at runtime based on the runtime types of both arguments. That is hard; the == operator is dispatched based on the compile time types of both operands, and the .Equals method is dispatched based on the runtime type of the receiver but the compile time type of the argument.

The standard way of implementing double dispatch in a language that supports only single virtual dispatch is the Visitor Pattern. You might look into that.

For further reading on this subject you might check out my article here:

http://blogs.msdn.com/b/ericlippert/archive/2009/04/09/double-your-dispatch-double-your-fun.aspx

Eric Lippert
Given the requirement that the two types are the same, I don't think it's so difficult in this case - both `Task.Equals(TaskA)` and `TaskA.Equals(Task)` (in terms of runtime types) should return false due to the comparison via `GetType()` - so it doesn't matter which implementation spots that the types are different. It's certainly harder in the more general case.
Jon Skeet
Wouldn't it be easy enough to just use `dynamic` here to get behavior based on the runtime types?
Gabe
@Gabe: yes, that would work as well.
Eric Lippert
A: 

If you can make Task an abstract class the template method pattern might be a good one to follow.

public abstract class Task
{
    string Name;
    protected abstract bool DoEquals( Task rhs );

    public static bool operator ==(Task t1, Task t2)
    {
        return t1.DoEquals( t2 );
    }
}

public class TaskA : Task
{
    protected bool DoEquals( Task rhs )
    {
        return /* custom compare */
    }
}

public class TaskB : Task
{
    protected bool DoEquals( Task rhs )
    {
        return /* custom compare */
    }
}
Jerod Houghtelling
Task is instantiated, and i'm using Fwx3.5 so i guess i'll need to do something like this or Skeet's. I've just have to tell clients to call the method and not to call the == operator, to avoid any confusion.
rediVider
A: 

My hybrid solution that seems to work.

public class Task
{
  string Name;
  public static bool operator ==(Task t1, Task t2)
  { 
    if ((object)t1 == null || (object)t2 == null)
    {
      return (object)t1 == null && (object)t2 == null;
    }

    return t1.Name == t2.Name && t1.GetType() == t2.GetType(); }
  public virtual bool Equals(Task t2)
  {
     return this == t2;
  }
}

public class TaskA : Task
{
  int aThing;
  public static bool operator ==(TaskA t1, TaskA t2)
  { 
    if ((object)t1 == null || (object)t2 == null)
    {
      return (object)t1 == null && (object)t2 == null;
    }

    return (Task)t1 == (Task)t2 && t1.aThing == t2.aThing;
  }
  public override bool Equals(Task t2)
  {
    return this == t2 as TaskA;
  }
}
rediVider