views:

42

answers:

2

I have a helper class that does a simple but repetitive process on a List of entities. For simplicity, it's like this...

public static List<MyType> DoSomethingSimple(List<MyType> myTypes) {
    return myTypes.Where(myType => myType.SomeProperty.Equals(2)).ToList();
}

I now need to add support for another type, but everything is identical... how do I avoid an increasing list of overloaded methods like this:

public static List<MyType> DoSomethingSimple(List<MyType> myTypes) {
    return myTypes.Where(myType => myType.SomeProperty.Equals(2)).ToList();
}

public static List<MyOtherType> DoSomethingSimple(List<MyOtherType> myOtherTypes) {
    return myOtherTypes.Where(myOtherType => myOtherType.SomeProperty.Equals(2)).ToList();
}

... and so on.

+2  A: 

Assuming the property has the same name and type for each list type, you could add an interface containing the property and implement it for each type you want to call this method on:

public interface ISomeProperty
{
    object SomeProperty { get; }
}

DoSomethingSimple could then be:

public static List<T> DoSomethingSimple<T>(IEnumerable<T> list) where T : ISomeProperty
{
    return list.Where(i => i.SomeProperty.Equals(2)).ToList();
}
Lee
+4  A: 

Here's two ways:

  1. Use generics, and a common base class
  2. Use interfaces

Method 1:

public class BaseClass
{
    public int SomeProperty { get; set; }
}

public class MyType : BaseClass { }
public class MyOtherType : BaseClass { }

public class ClassWithMethod
{
    public static List<T> DoSomethingSimple<T>(List<T> myTypes)
        where T : BaseClass
    {
        return myTypes.Where(myType => myType.SomeProperty.Equals(2)).ToList();
    }
}

Method 2:

public interface ICommon
{
    int SomeProperty { get; set; }
}

public class MyType : ICommon
{
    public int SomeProperty { get; set; }
}

public class MyOtherType : ICommon
{
    public int SomeProperty { get; set; }
}

public class ClassWithMethod
{
    public static List<T> DoSomethingSimple<T>(List<T> myTypes)
        where T : ICommon
    {
        return myTypes.Where(myType => myType.SomeProperty.Equals(2)).ToList();
    }
}

Now, if you try to make the method use the interface directly, like this:

public class ClassWithMethod
{
    public static List<ICommon> DoSomethingSimple(List<ICommon> myTypes)
    {
        return myTypes.Where(myType => myType.SomeProperty.Equals(2)).ToList();
    }
}

Then that would work if you have a List<ICommon> when you call it, but won't work if you have a List<MyType>. In C# 4.0 this can be done if we change the method slightly:

public class ClassWithMethod
{
    public static List<ICommon> DoSomethingSimple(IEnumerable<ICommon> myTypes)
    {
        return myTypes.Where(myType => myType.SomeProperty.Equals(2)).ToList();
    }
}

Note that I changed to using an IEnumerable<ICommon> instead. The concept here is called Co- and contra-variance, and beyond that I'm not going to say much about it. Search Stack Overflow for more information on the subject.

Tip: I would change the input parameter to be IEnumerable<T> regardless, since this would make your method usable in more instances, you could have different types of collections, arrays, etc. and as long as they contain the right type, they can be passed to the method. By limiting yourself to List<T> you force the user of your code to convert to a list in some cases. My guidelines are to be as unspecific as possible in input parameters, and as specific as possible in output parameters.

Lasse V. Karlsen