当类实现所有接口但未声明接口时,将其适应接口

时间:2018-10-05 20:11:37

标签: c# dictionary interface hashcode strong-typing

设置以下接口:

interface IFoo
{
    void Foo();
}

interface IBar
{
    void Bar();
}

interface IFooBar : IFoo, IBar
{
    // No extra required feature.
}

和班级:

class Booh : IFoo, IBar
{
    public void Foo() { }
    public void Bar() { }
}

尽管Booh实现了IFooBar所需的一切,但我无法将Booh用作IFooBar,因为它没有正式实现。

为了允许在不将Booh更改为IFooBar的情况下将Booh用作class Booh : IFooBar,我想(基于另一个SO问题)考虑编写一个包装器:

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(); }
}

问题是我可以照常做!

例如,如果我将此包装器类用作字典键,它将使用包装器的引用而不是包装对象的引用。

如果我先做someDictionary.Add(new FooBar<Booh>(someBooh), whatever);,然后再做someDictionary.Remove<Booh>(new FooBar(someBooh));,它将不会删除我最初添加的Booh,因为我创建了两个不同的包装器,每个包装器都有自己的地址

要解决此问题,我已经重写/实现了一些用于相等性检查和哈希码的方法:

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();
    }
}

这可能导致包装的对象引用被字典使用,我尚未测试过。

所以,我的问题是:为了覆盖大多数(如果不是全部)用例,还需要重写和/或实现其他方法吗?我希望包装器的行为就像包装的对象本身,而不是另一个对象。谢谢!

编辑:也许我可以改为使它成为一个结构,并依靠自动装箱将包装器结构包装到一个对象中,该对象将其哈希码和相等性检查方法委派给该结构,从而使用包装对象引用?

1 个答案:

答案 0 :(得分:0)

是的,您所要做的三种方法就是您所需要的。据称字典主要依赖于哈希码。

但是,您在Equals(object obj)中的强制转换会出错:它将Booh强制转换为null。您要同时测试/发布{em> FooBar<T>T

JetBrains Rider提供或多或少的功能:

    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);
    }

通过了哪些测试:

    [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);
    }

我不太清楚使用struct可以在两种方式上都没有太大的不同。您仍然必须以相同的方式覆盖Equality成员。

我添加了

static class BoohFooBarExt
{
    public static IFooBar AsIFooBar<T>(this T value ) where T:IFoo, IBar => new FooBar<T>(value);
}