Дженерик метод с классами интерфейса
Пилю игру на 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 шт):
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
и тогда интерфейс реализует адаптер берущий значения по ключам, которые ему указали.