views:

154

answers:

7

I know this is probably a really simple question but I'm having a brain fart at the moment. I am trying to create a method that can take one of 2 custom types. Basically the body of this method will be identical for both the types as they both have a Name property (I'm doing a comparison on the Name property to use in sorting). How should I do this?

My first thought was just to overload the method with the two types as arguments:

int Compare(Type1 first, Type1 second)
int Compare (Type2 first, Type2 second)

but the body of the methods ended up being identical thus it seems like a waste.

My next thought was to use generics but that doesn't seem right because I'm not really making it generic as it can only be used with 2 specific types.

Clarification: The "custom" types are actually not my custom types. What I meant was taht they are not built-in types. I do not have control over what is in these types or the inheritence hierarchy. They just both happen to have the Name property.

+5  A: 

Generic is still an option if the two types derive from the same base or use the same interface, and no other classes use the base and/or interface.

int Compare<T>(T first, T second) where T : IHasName

Short of that, having the two overloads is the way to go.

Note: If you have .NET 4, you could make it a runtime thing and make the method dynamic.

int Compare(dynamic first, dynamic second)

In which case anything you throw at it will compile, but it will blow up at runtime if the Name property is not present.

Personally, if I can't modify the type to use a common interface, then I would go with the overloads and get compile-time safety.

Anthony Pegram
Making both types implement a common interface seems like the way to go IMHO.
Chris Shouts
Both not a possibility. They do not derive from the same base class and the two types are not written by me so I can't change that.
KrisTrip
Even if the types are not written by you, you can still take the overload route?
Bertvan
@Kris, added a note about .NET 4 and dynamic. But in your situation, I would probably stick with overloads.
Anthony Pegram
Overloading would still be an option, but you wouldn't be able to consolidate the code for those two methods. If those two types of objects are similar enough that you can write a method for each type using nearly identical code, then I am in agreement with Chris; they should implement a common interface. Can you get the author to update the declaration of those classes? If these are really disconnected components where you will have no way to make them share a common interface, I think the best you could do is write some kind of wrapper class around each type.
Dr. Wily's Apprentice
I ended up just going the two overload route but I appreciate the additional options you provided so I'm marking yours as the best answer.
KrisTrip
+1  A: 

If it can only be used with 2 types, then make 2 overloads.

Bertvan
I did end up going this route so +1 for you but I marked the answer with more options as correct as the other options may help someone else more in the future.
KrisTrip
A: 

Maybe not the best solution but this is what comes to my mind: You make a method like:

int MainCompare(Type1 first1, Type1 second1, Type2 first2, Type2 second2, bool firsttype){...}

Then you can can have the two Compare methods with one line.

int compare(Type1 first, Type1 second){
    return MainCompare(first, second, null, null, true);
}


int compare(Type2 first, Type2 second){
    return MainCompare(null, null, first, second, false);
}

your MainCompare will just change a bit depending on what type it's using.

Auxiliary
+1  A: 

Overloading the method would be a good way to make it take only two specific types. To avoid repeating the code, you can use generics behind the scene:

public int Compare(Type1 first, Type1 second) {
  return Compare<Type1>(first, second);
}

public int Compare(Type2 first, Type2 second) {
  return Compare<Type2>(first, second);
}

private int Compare<T>(T first, T second) {
  ...
}
Guffa
You'll still need to restrict `T` to a base interface/class to be able to call specific methods on it...
Porges
+3  A: 

Okay, I read your question completely wrong in the first answer. Here's a better one :)

Overloads are your best bet. Since the comparison result only depends upon Name, make a method that does that comparison, then call it:

private int Compare(string first, string second)
{
    // do comparison
}

public int Compare(Type1 first, Type1 second)
{
    return Compare(first.Name, second.Name):
}

public int Compare(Type2 first, Type2 second)
{
    return Compare(first.Name, second.Name);
}

Edit:

Since you have more than one item, there's two things you can do:

public int Compare(string first1, string second1, X first2, X second2 ...

But that's going to get a bit ugly. An alternative is to provide a projection to extract the values:

private int Compare<T>(T first, T second, Func<T,Tuple<string,int,TypeX,TypeY>> projection)
{
    // test for first==null, second==null, etc...

    var nicerFirst = projection(first);
    var nicerSecond = projection(second);

    // compare objects using nicerFirst.Item1, nicerFirst.Item2, etc.
}

Then your compare looks something like:

int Compare(Type1 first, Type1 second)
{
    return Compare(first, second, x => Tuple.Create(x.Name, x.Int, x.X, x.Y));
}
Porges
There are actually additional comparisons I make other than Name so this doesn't quite work. Helped out though so +1, Thanks!
KrisTrip
I'll expand my answer :)
Porges
A: 

This is probably an overboard solution, but you could make adapters for the two classes, and have them implement a common interface:

public class MyType1 : ICommonInterface, Type1
{
    // Where you can define 'Somethin' in the common interface
    public string Something 
    { 
        get { return base.Something; } 
        set { base.Something = value; } 
    }

     /* ... */
}

public class MyType2 : ICommonInterface, Type2
{
    // If there is no base.Something on Type2, you can re-name it assuming 
    // its intent is the same as Something
    public string Something
    {
        get { return base.SomethingElse; } 
        set { set base.SomethingElse = value; }
    }        
}

Then you can implement basically exactly what @Anthony Pegram (+1) had:

int Compare<T> (T first, T second) where T : ICommonInterface
{
    return first.Something.CompareTo(second.Something);
}
SnOrfus
+7  A: 

Oddly enough so far no one has posted what seems to me to be the obvious choice: take the name as the argument of the comparison. Make the caller get the name property out of the object. If all the method needs to use is the name then why are you passing in the rest of the object?

If you need to abstract over the notion of "I can get a name out of this thing" then do that:

Example:

int Compare(string name1, string name2) { ... whatever ... }
int Compare<T>(T t1, T t2, Func<T, string> getName)
{
    return Compare(getName(t1), getName(t2));
}

And now you can say:

Compare(car1, car2, c=>c.Name);
Compare(employee1, employee2, e=>e.Name);

and so on.

Eric Lippert
@Porges did basically post this response (or fairly similar). There are actually additional comparisons I make other than Name so this doesn't quite work. +1 for simplicity though.
KrisTrip