Reputation: 5043
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.
Upvotes: 4
Views: 5030
Reputation: 660038
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.
Upvotes: 9
Reputation: 30580
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));
}
Upvotes: 4
Reputation: 17196
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);
}
Upvotes: 0
Reputation: 700322
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) {
...
}
Upvotes: 1
Reputation: 126844
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.
Upvotes: 7
Reputation: 2757
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.
Upvotes: 0