Let the following interfaces :
interface IFoo
{
void Foo();
}
interface IBar
{
void Bar();
}
interface IFooBar : IFoo, IBar
{
// No extra required feature.
}
And the class :
class Booh : IFoo, IBar
{
public void Foo() { }
public void Bar() { }
}
I cannot use Booh
as a IFooBar
, despite Booh
implementing everything required by IFooBar
, because it does not officially implement it.
In order to allow the use of a Booh
as a IFooBar
without changing Booh
to class Booh : IFooBar
, I have thought (based on another SO question) about writing a wrapper :
class FooBar<T> : IFooBar where T : IFoo, IBar
{
public T Value { get; private set; }
public FooBar(T value)
{
Value = value;
}
public void Foo() { Value.Foo(); }
public void Bar() { Value.Bar(); }
}
The problem with that is that I can us as is !
For exemple, if I use this wrapper class as a dictionary key, it will use the reference of the wrapper instead of the reference of the wrapped object.
If I do : someDictionary.Add(new FooBar<Booh>(someBooh), whatever);
and then someDictionary.Remove<Booh>(new FooBar(someBooh));
it will not remove the Booh
I added in a first place, because I created two different wrappers, each of them having its own address.
To work around this, I have overriden / implemented some methods for equality checks and hash codes :
class FooBar<T> : IFooBar where T : IFoo, IBar
{
// Same as above...
public bool Equals(FooBar<T> other)
{
return Value.Equals(other.Value);
}
public override bool Equals(object obj)
{
var cast = obj as FooBar<T>;
if (null != obj && null == cast || obj == null)
{
return false;
}
return Value.Equals(cast.Value);
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
This supposedly causes the wrapped object reference be used by dictionaries, I haven't tested yet.
So, my question is : are there other methods I need to override and / or implement in order to cover most (if not all) use cases ? I want that wrapper to behave like it was the object being wrapped itself, not another object. Thank you !
EDIT : Maybe I could instead make this a struct and rely on auto-boxing to wrap the wrapper struct into an object that will delegate it's hash code and equality check methods to the structure and thus use the wrapped object reference ?
Yes, the 3 methods you've done is all you need. A dictionary allegedly relies mostly on the hashcode.
However your cast in Equals(object obj)
will go wrong: it will cast a Booh
to null. You want to test/cast both FooBar<T>
and just plain T
.
JetBrains Rider offer more or less this:
bool Equals(FooBar<T> other)
{
return EqualityComparer<T>.Default.Equals(Value, other.Value);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj is T) return Value.Equals(obj);
if (obj.GetType() != this.GetType()) return false;
return Equals((FooBar<T>) obj);
}
public override int GetHashCode()
{
return EqualityComparer<T>.Default.GetHashCode(Value);
}
Which passes these tests:
[Fact]
public void CanUseInDict()
{
var foobar= new Booh();
IFooBar[] foobars= new IFooBar[]{ foobar.AsIFooBar() };
Dictionary<IFooBar,string> ifoobars= new Dictionary<IFooBar, string>()
{
{ foobar.AsIFooBar(), foobar.GetType().Name}
};
Assert.Equal( foobar.GetHashCode(), new FooBar<Booh>( foobar ).GetHashCode());
Assert.True( foobar.AsIFooBar().Equals( new FooBar<Booh>( foobar ) ) , "Equals FooBar<Booh>");
Assert.True( ifoobars.ContainsKey( new FooBar<Booh>(foobar) ), "ContainsKey");
ifoobars.Remove(foobar.AsIFooBar());
Assert.Empty(ifoobars);
}
I can't quite see that use struct instead makes much difference either way. You still have to override the Equality members the same.
I added
static class BoohFooBarExt
{
public static IFooBar AsIFooBar<T>(this T value ) where T:IFoo, IBar => new FooBar<T>(value);
}