Дженерик метод с классами интерфейса

Пилю игру на Unity. Решил описать несколько почти одинаковых классов через интерфейсы.

Идея: Есть компоненты HealthComponent и ManaComponent наследуемые от интерфейса IIndicatorComponent. Необходимо реализовать отображение их переменных через классы HpBar и MpBar наследуемых от IIndicatorBar.

Реализация:

public interface IIndicatorBar
{
     public void Initialize<T>(T component) where T : IIndicatorComponent;
     ...
}

public class HpBar : MonoBehaviour, IIndicatorBar
{
    [SerializeField] private HealthComponent _healthComponent;

    public void Initialize<T>(T healthComponent) where T : HealthComponent
    {
        _healthComponent = healthComponent;                                        
        Refresh();
    }
...
}

Проблема: Метод Initialize выдаёт ошибку CS0425 мол ограничения метода отличаются от того что описано в интерфейсе, однако в этом классе мне нужен конкретно HealthComponent, который является дочерним тому что указан в интерфейсе.В классе MpBar ситуация аналогична. Если заменить класс на тот что в интерфейсе, то переменная _healthComponent будет жаловаться на несоответствие классов.

Вопрос: Что нужно сделать, чтобы можно было корректно присваивать переменным _healthComponent и _manaComponent из другого класса их входные данные?


Ответы (1 шт):

Автор решения: Yaroslav
public interface IFoo<T> 
{
    void DoSome(T arg);
}
public class FooStr : IFoo<string>
{
    public void DoSome(string arg) { }
}

public class FooInt : IFoo<int>
{
    public void DoSome(int arg) { }
}

Но экземпляры классов реализующие IFoo<string> и IFoo<int> это представители двух разных классов не имеющих общего потомка/интерфейса.


У тебя в корне не верный DI. Чем вообще отличаются HpBar, MpBar, StaminaBar, BreathBar и т.д... ? НИЧЕМ! Вся суть бара это брать значение и максимальное значение, меняя слайдер, как бы он не выглядил, это должен быть один класс и интерфейс ему не нужен! Нужен только интерфейс для взятия значений!

public class Bar : MonoBehaviour
{
    [SerializeField] private Slider _slider;
    [SerializeField] private Text _values;
    private IFloatingProperty _property;

    private void OnEnable ()
    {
        if (_property != null)
        {
            _property.Changed += UpdateValue;
            UpdateValue();
        }
    }

    private void OnDisable () 
    { 
        if (_property != null)
            _property.Changed -= UpdateValue;
    }
    
    public void SetProperty (IFloatingProperty property)
    {
        OnDisable();
        _property = property;
        OnEnable();
    }

    private void UpdateValue ()
    {
        if (_slider != null)
            _slider.value = _property.Value / _property.Max;
        if (_values != null)
            _values.text = string.Format("{0} / {1}", _property.Value, _property.Max);
    }
}
public interface IFloatingProperty
{
    event Action Changed; // по желанию можно сделать отрисовку событийной

    float Value { get; }
    float Max { get; }
}

Само хп и остальное, может быть чем угодно, сущностью принимающую статы для максимального значения и регена, которая должна реализовывать интерфейс. Или просто значением в каком-нибудь справочнике типа Unit.Propertys и тогда интерфейс реализует адаптер берущий значения по ключам, которые ему указали.

→ Ссылка