2

I want create a Func that is a two-argument encapsulation of the base.Equals method for some derived class. One argument of the Func is the instance in/on which base.Equals is called and the other argument of the Func is passed into base.Equals. I cannot place requirements on the base class, but I can place some on the derived class. For example, I can require the derived class to expose base.Equals.

This would be easy if the keyword base could be used in like the keyword this, but it doesn't. See my failed attempt below.

using System; namespace MyNamespace { class BaseClass { } class DerivedClass : BaseClass, IEquatable<DerivedClass> { Func<DerivedClass, DerivedClass, bool> baseEquals; DerivedClass() { DerivedClass referenceToThis = this; BaseClass referenceToBase = referenceToThis.base; // doesn't compile this.baseEquals = (x, y) => x.Equals(y); } public bool Equals(DerivedClass that) => this.baseEquals(this, that); } } 

Based on my current understanding of C#, I think what want to do is not allowed.

Is there C# that has the same semantics as my example but is valid?

If not, how close can one get to this example? That is, to what extent can the base.Equals method be encapsulated?

Edit to add an example

using System; using FluentEquality.Common.EqualityCompareres; namespace MyNamespace { class MyProgram { static void Main(string[] args) { var instance0 = new LeafClass(0); var instance1 = new LeafClass(1); var baseEqualsMethod = SomeMagicalMethodYetToBeDefined(instance0.BaseEqualsMethod, instance0.BaseGetHashCodeMethod); equalityComparer.Equals(instance0, instance1); // should output: BaseClass 0 equalityComparer.Equals(instance1, instance0); // should output: BaseClass 1 Console.ReadKey(); } } class BaseClass { protected int Id; public BaseClass(int id) { this.Id = id; } public override bool Equals(object obj) { Console.WriteLine(nameof(BaseClass) + " " + Id); return false; } public override int GetHashCode() => base.GetHashCode(); } class DerivedClass : BaseClass { public DerivedClass(int id) : base(id) { } public override bool Equals(object obj) { Console.WriteLine(nameof(DerivedClass) + " " + Id); return false; } public override int GetHashCode() => base.GetHashCode(); public Func<object, bool> BaseEqualsMethod => base.Equals; public Func<int> BaseGetHashCodeMethod => base.GetHashCode; } class LeafClass : DerivedClass { public LeafClass(int id) : base(id) { } public override bool Equals(object obj) { Console.WriteLine(nameof(LeafClass) + " " + Id); return false; } public override int GetHashCode() => base.GetHashCode(); } } 
7
  • 1
    It seems fishy to me that you want to always call the base.Equals, without knowing the type. Could you explain the broad solution you're looking for? I'm sure there's a better way to approach the issue. Forgetting that Equals is a method on object, what happens if they pass you BaseClass, where there is no base.Equals? Commented May 17, 2016 at 3:48
  • At a quick glance, this would appear to lead to a Stack Overflow exception if slightly modified. Perhaps if you explained what you actually need, you'd get a better answer. Commented May 17, 2016 at 3:53
  • @Rob Of course I don't want to always call base.Equals. I only did that to create a minimum working example. I want to create an implantation of IEqualityComparer that uses the base Equals and GeyHashCode methods for a type. Your last question raised a good point in that whatever I build will also need to work for the type object, which has no base. Commented May 17, 2016 at 3:54
  • @David I don't care if the MWE has a stack overflow. I only included the IEquatable<>.Equals method definition so show the evaluation if the Func. Commented May 17, 2016 at 4:04
  • In your new example, did you intend baseEqualsMethod and equalityComparer to be the same thing? Commented May 17, 2016 at 15:10

3 Answers 3

2

So, my other answer was fatally flawed in that it had the same problem as reflection did - the overridden method would be called, regardless of which method you attempted to invoke.

The following is an example using the technique found here to create a dynamic method which does allow you to specify which overridden method is to be called. The benefit of this approach is that you do not need the classes to expose the base method, and in-fact - you don't need to modify the classes at all.

class BaseClass { public override bool Equals(object other) { Console.WriteLine("BaseClass"); return false; } } class DerivedClass { public bool Equals(DerivedClass other) { Console.WriteLine("DerivedClass Equals"); return true; } public override bool Equals(object other) { Console.WriteLine("DerivedClass Object Equals"); return true; } } static class MyComparerThing<TParentType> { public static bool Equals(TParentType left, TParentType right) => MyComparerThing<TParentType, TParentType, object>.Equals(left, right); } static class MyComparerThing<TParentType, TOnType> { public static bool Equals(TOnType left, TOnType right) => MyComparerThing<TParentType, TOnType, object>.Equals(left, right); } static class MyComparerThing<TParentType, TOnType, TCompareType> { static Func<TOnType, TOnType, bool> baseEquals; static MyComparerThing() { DynamicMethod dm = new DynamicMethod("BaseFoo", typeof(bool), new Type[] { typeof(TOnType), typeof(TOnType) }, typeof(TOnType)); ILGenerator gen = dm.GetILGenerator(); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Ldarg_1); var method = typeof(TParentType).GetMethod("Equals", new[] { typeof(TCompareType) }); gen.Emit(OpCodes.Call, method); gen.Emit(OpCodes.Ret); baseEquals = (Func<TOnType, TOnType, bool>)dm.CreateDelegate(typeof(Func<TOnType, TOnType, bool>)); } public static bool Equals(TOnType left, TOnType right) => baseEquals(left, right); } 

Testing with the following:

void Main() { var a = new DerivedClass(); var b = new DerivedClass(); //The generic parameters in order are: //1. The type which implements the desired method //2. The type of the arguments (a and b) //3. The parameter argument of the method (object vs DerivedClass) //The following three are equivelant, using 'default' generic arguments MyComparerThing<DerivedClass>.Equals(a, b); MyComparerThing<DerivedClass, DerivedClass>.Equals(a, b); MyComparerThing<DerivedClass, DerivedClass, object>.Equals(a, b); //This will print the method declared in BaseClass. a and b are still DerivedClass instances. This is the one you're wanting to use MyComparerThing<BaseClass, DerivedClass>.Equals(a, b); //This will print the method declared in DerivedClass, with the DerivedClass overload MyComparerThing<DerivedClass, DerivedClass, DerivedClass>.Equals(a, b); //This will print the method declared in DerivedClass, with the object overload MyComparerThing<DerivedClass, DerivedClass, object>.Equals(a, b); } 

Prints:

DerivedClass Object Equals DerivedClass Object Equals DerivedClass Object Equals BaseClass DerivedClass Equals DerivedClass Object Equals 

I've chosen to use generics to decide which override to call - but this can be changed to pull the information from elsewhere, or alternatively, to hardcode it. If you want to always call the base method, you'd need to modify the above to write something along the lines of typeof(TOnType).BaseType.GetMethod("Equals", ....)

Sign up to request clarification or add additional context in comments.

3 Comments

Tested and works. An advantage of @StriplingWarrior 's answer is that it is not necessary to specify the type on which the overloaded method exists. Suppose someone refactored the inheritance by (1) adding another Equals overload on a type between BaseClass and DerivedClass, (2) removing the overload from BaseClass, or (3) changing the name of BaseClass. These would be cause compile or, even worse, runtime errors for the users of this code. To avoid this, couldn't we look at the MSIL generated by StriplingWarrior's solution and use that? Surely it isn't using callvert.
@TysonWilliams: In the IL code generated by my answer DerivedClass.Equals in my answer does a call to BaseClass.Equals. This answer does the same thing via gen.Emit(OpCodes.Call, method). Rob used generics to specify the parent class for the sake of this example, but you could easily determine it using typeof(TOnType).BaseType instead of typeof(TParentType). I think the time has come to mark this question as answered.
@StriplingWarrior It only happens to be the same in this case. In general, the most recent overload of Object.Equals(object) could be on any parent class in the inheritance hierarchy. Using reflection, I could write the code needed to determine the correct type to pass in to Rob's solution (as an argument of type Type instead of a type parameter). Clearly the compiler contains this code. I wonder if it is available to be called. Anyway, the solutions by Rob and yourself definitely clarify how the base keyword works and what can be accomplished with it. Thanks!
2

I think you're overcomplicating things. If I understand correctly, all you're really trying to do is implement IEquatable<DerivedClass>, but delegate to the base.Equals() method:

 class BaseClass { } class DerivedClass : BaseClass, IEquatable<DerivedClass> { public bool Equals(DerivedClass that) => base.Equals(that); } 

Update

If you are willing to require your child classes to implement a method exposing their base class's method, then you can just invoke that method from your equality comparer:

void Main() { // Output: "Base" new BaseEqualityComparer<DerivedClass>().Equals( new DerivedClass(), new DerivedClass()); } class BaseClass { public override bool Equals(object that) { Console.WriteLine("Base"); return base.Equals(that); } } class DerivedClass : BaseClass, IEquatable<DerivedClass> { public override bool Equals(object that) { Console.WriteLine("Derived"); return base.Equals(that); } public bool Equals(DerivedClass that) => base.Equals(that); } class BaseEqualityComparer<T> : IEqualityComparer<T> where T : IEquatable<T> { public bool Equals(T val1, T val2) { return val1.Equals(val2); } public int GetHashCode(T val) { throw new NotImplementedException(); } } 

However, if you're not willing to make that a requirement for all the base classes, I'm pretty sure C# won't let you do this directly. Rob's answer shows that it is possible to capture the call to the base method's implementation, so it's clearly something that the runtime is capable of. I believe if you're willing to emit IL code, you should be able to do what you want by issuing a call instruction (rather than a callvirt instruction) to the base method.

17 Comments

No, that is definitely not what I am trying to do.
@TysonWilliams Based on your comment on the question - I want to create an implantation of IEqualityComparer that uses the base Equals and GeyHashCode methods for a type - How does that differ from this answer?
The simplest way to show that is to observe that your answer doesn't even include the string "IEqualityComparer".
@TysonWilliams Okay - but then I fail to understand how it will be both an IEqualityComparer and an IEquatable. If it's an IEqualityComparer, then it's hard-coding the fact that you're calling base.Equals (even without knowing the type). But your comment on the question is says you don't always want to call base.Equals. Could you perhaps show us a list of use-cases and what the method should return for each? Because as I see it, there's no clear explanation of how it should behave.
Oh, actually, I did look at your link, but had never seen MSIL before and didn't want to go down the path of directly using it. In contrast though, I am ok with this MSIL generator that Rob is using.
|
2

You could cast this to your base class and get similar behavior.

13 Comments

What if I don't know the derived class until runtime? Then I wouldn't know what base class to cast to. (Updated question to say this.)
@TysonWilliams: How can you define a derived class without knowing what its base class is at compile-time?
The base and derived classes are defined elsewhere by others. I only want to depend on the type, not the dentition of the type.
I may have misspoken here. I tried to update the question to clarify. In particular, I cannot place any requirements on the base class, but I can require those that implement the derived class to jump through some hoops. As a primary design goal, I would like things to be type safe (at least between the API boundary between the code in the derived class and the code that creates the Func that I want), so casting is out of the question. If it came to that, then I would rather sacrifice the design in other ways.
@TysonWilliams You could preserve type safety with the as operator and null checking/propagation. This would meet the requirement of only the derived class jumping through hoops.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.