Адаптация класса к интерфейсу, когда он реализует весь интерфейс, но не объявляет интерфейс - PullRequest
0 голосов
/ 05 октября 2018

Позвольте следующие интерфейсы:

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, я подумал (основываясь наеще один вопрос) о написании оболочки:

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

Этопредположительно вызывает использование ссылки на завернутый объект by словари, я еще не тестировал.

Итак, мой вопрос: Есть ли другие методы, которые мне нужно переопределить и / или реализовать, чтобы охватить большинство (если не все)случаи применения ?Я хочу, чтобы эта оболочка вела себя так, как будто это был сам объект, а не другой объект.Спасибо!

РЕДАКТИРОВАТЬ: Может быть, я мог бы вместо этого сделать это структурой и полагаться на автобокс, чтобы обернуть структуру-обертку в объект, который делегирует ее хэш-код и методы проверки равенстваструктура и, следовательно, использовать ссылку на завернутый объект?

1 Ответ

0 голосов
/ 06 октября 2018

Да, 3 метода, которые вы сделали, это все, что вам нужно.Предполагается, что словарь опирается главным образом на хеш-код.

Однако, ваше приведение в Equals(object obj) пойдет не так, как положено Booh в ноль.Вы хотите протестировать / разыграть как 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);
}
...