0

I would like to be able to do the following for several classes:

var obj1 = new MyClass { Id = 1 };
var obj2 = new MyClass { Id = 2 };
obj1.Compare(obj2);

I made the following extension method (inspired by a different question inhere):

public static class ObjExt
{
    public static ICollection<string> Compare<T>(this T obj1, T obj2)
    {
        var properties = typeof(T).GetProperties();
        var changes = new List<string>();

        foreach (var pi in properties)
        {
            var value1 = typeof(T).GetProperty(pi.Name).GetValue(obj1, null);
            var value2 = typeof(T).GetProperty(pi.Name).GetValue(obj2, null);

            if (value1 != value2 && (value1 == null || !value1.Equals(value2)))
            {
                changes.Add(string.Format("Value of {0} changed from <{1}> to <{2}>.", pi.Name, value1, value2));
            }
        }
        return changes;
    }

Now, this works if I make a method in all classes I want to compare, so I figured I'd move it so a super class to DRY.

public class MyClass
{
    public int Id { get; set; }

    public ICollection<string> CompareMe<T>(T obj2)
    {
        return Compare<T>(obj2);
    }
}

If I move it to a super class, I get this compile error:

Cannot convert instance type argument 'SuperClass' to 'T'

If I do this in my super class:

return this.Compare<T>(obj2);

I get a compile error saying:

The type arguments for method 'Compare(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

How to make this generic in a super class?

Community
  • 1
  • 1

3 Answers3

1

Not sure how your super class looks. But this compiles fine:

public class SuperClass
{
    public bool GenericTest<T>(T obj2)
    {
       return ObjExt.GenericTest(obj2, obj2);
    }
}

public class MyClass : SuperClass
{
    public int Id { get; set; }

    public bool SuperTest<T>(T obj2)
    {
        return this.GenericTest<T>(obj2);
    }
}

public static class ObjExt
{
    public static bool GenericTest<T>(this T obj1, T obj2)
    {
        return true;
    }
}
Magnus
  • 45,362
  • 8
  • 80
  • 118
  • GenericTest is in a static extension class ObjExt - outside both MyClass and SuperClass. That seems to change the picture. – Nicolai Schlenzig Dec 03 '12 at 16:12
  • @NicolaiSchlenzig You write _I would like to extract it to a super class_. So I don't see how this extension method fits into the picture. – Magnus Dec 03 '12 at 18:16
  • You're right. I thought I could keep my extension method and call it from my super class to avoid redundant code. – Nicolai Schlenzig Dec 04 '12 at 07:55
  • The line `return ObjExt.GenericTest(obj2, obj2);` does not make sense to me. Obj2 twice? I tried putting "this" instead, but then I'm back to the same error :) – Nicolai Schlenzig Dec 04 '12 at 08:44
  • @NicolaiSchlenzig Makes no sense of course, but I thought this was just some example code. The thing is that both objects must be of the same type (T in this case) unless you set up some kind of generic constraint. – Magnus Dec 04 '12 at 08:57
  • Question updated with actual code. I just didn't want to clutter the question with many irrelevant lines. – Nicolai Schlenzig Dec 04 '12 at 09:38
1

This extension method:

public static bool GenericTest<T>(this T obj1, T obj2)
{
}

will not compile, because the compiler has no clue about what T really is: there is no context to infer types from. You either need to use something like where T: SuperClass or change the method parameters to this SuperClass obj1, SuperClass obj2.

Christoffer
  • 2,125
  • 15
  • 21
  • That makes sense, but then SuperClass is the only class being able to use the extension method. I was hoping to make it more generic if possible. Moving the method into SuperClass makes more sense (to me). – Nicolai Schlenzig Dec 04 '12 at 07:41
1

You can add a generic constraint on the SuperTest method:

public bool SuperTest<T>(T obj2) where T: SuperClass
            {
                return this.GenericTest(obj2);
            }

And replace T in the extensionmethod with SuperClass:

public static bool GenericTest(this SuperClass obj1, SuperClass obj2)
        {
            return true;
        }

I'm not sure if this is what you had in mind though.

Panos Rontogiannis
  • 4,154
  • 1
  • 24
  • 29
  • I wanted to make it as generic as possible - not using SuperClass in the extension at all. I thought I could do this by introducing a method in SuperClass calling the extension method. – Nicolai Schlenzig Dec 04 '12 at 07:40
  • I read your updated question and given your reflection-based implementation I don't see why you need the CompareMe method at all. You can remove it and keep everything else. – Panos Rontogiannis Dec 04 '12 at 11:52
  • That is what I concluded from reading Matthew Nichols comment. However, his answer was removed so I could not mark it solved. – Nicolai Schlenzig Dec 04 '12 at 13:10
  • 1
    Sadly my answer was decided to be off-topic by a moderator even though it solved you problem. – Matthew Nichols Dec 06 '12 at 23:23