【问题标题】:Adapting a Class to an Interface when it implements all of the interface but doesn't declare the interface当类实现所有接口但未声明接口时,将类调整为接口
【发布时间】:2019-03-11 09:06:54
【问题描述】:

让下面的接口:

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 更改为class Booh : IFooBar 的情况下将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&lt;Booh&gt;(someBooh), whatever); 然后someDictionary.Remove&lt;Booh&gt;(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();
    }
}

这可能会导致字典使用包装的对象引用,我还没有测试过。

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

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

【问题讨论】:

    标签: c# dictionary interface hashcode strong-typing


    【解决方案1】:

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

    但是你在Equals(object obj) 中的转换会出错:它会将Booh 转换为null。你想测试/投射 both FooBar&lt;T&gt; 和简单的 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);
        }
    

    我不太明白 use struct 会带来多大的不同。您仍然必须以相同的方式覆盖 Equality 成员。

    我加了

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

    【讨论】:

    • 感谢详细解答!
    猜你喜欢
    • 2013-11-13
    • 2019-03-26
    • 2017-05-28
    • 1970-01-01
    • 2013-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-17
    相关资源
    最近更新 更多