Reputation: 163
I am trying to refactor some code and have to admit that I am new to the concept of generics.
I have a base class:
BaseVarDef.cs:
using UnityEngine;
public abstract class BaseVarDef<T> : ScriptableObject
{
[Multiline]
public string DeveloperDescription = "";
public T Value;
public void SetValue(T value)
{
Value = value;
}
}
Then I have several classes that derive from this. But they all contain the same 3 methods that I would like to refactor into this generic base class. The only difference is they expect either the generic type or class that is currently defining them.
FloatVar.cs
using UnityEngine;
[CreateAssetMenu(fileName = "New FloatVar", menuName = "Variables/FloatVar")]
public class FloatVar : BaseVarDef<float>
{
public void SetValue(FloatVar value)
{
Value = value.Value;
}
public void ApplyChange(float amount)
{
Value += amount;
}
public void ApplyChange(FloatVar amount)
{
Value += amount.Value;
}
}
StringVar.cs
using UnityEngine;
[CreateAssetMenu(fileName = "New StringVar", menuName = "Variables/StringVar")]
public class StringVar : BaseVarDef<string>
{
public void SetValue(StringVar value)
{
Value = value.Value;
}
public void ApplyChange(string amount)
{
Value += amount;
}
public void ApplyChange(StringVar amount)
{
Value += amount.Value;
}
}
Is there a way to refactor the: SetValue and the 2 overloaded ApplyChange methods into BaseVarDef.cs?
Thanks in advance
Upvotes: 1
Views: 55
Reputation: 37030
If you can just add those methods to the original base class (and then remove the abstract
from it), and then you just do BaseVarDef<string>
or BaseVarDef<float>
. No more need for the FloatVar
or StringVar
classes!
The tricky part here is the +=
operator, which can't be applied to generics (at least there is no constraint I'm aware of that allows you to specify the type implements an operator).
So you could either make an abstract Add
method that derived classes must implement as Svetlana describes, or you can use dynamic
as shown below (and get rid of the need for derived classes altogether).
The one problem with dynamic
is that there is no compile time checking with dynamic
types, so it's not foolproof. The type T
must overload the +
operator or you'll get a runtime exception. You could avoid that by wrapping the line Value = amt + val;
in a try/catch
, but then you may get unexpected behavior.
public class BaseVarDef<T>
{
public string DeveloperDescription { get; set; } = "";
public T Value { get; private set; }
public void SetValue(T value)
{
Value = value;
}
public void SetValue(BaseVarDef<T> value)
{
Value = value.Value;
}
public void ApplyChange(T amount)
{
AddToValue(amount);
}
public void ApplyChange(BaseVarDef<T> amount)
{
AddToValue(amount.Value);
}
private void AddToValue(T amount)
{
dynamic amt = amount;
dynamic val = Value;
// Empty catch to avoid runtime exception if 'T' doesn't support the '+' operator
try { Value = amt + val; }
catch { }
}
}
Upvotes: 1
Reputation: 1046
I think you can define all Apply and Set in Base class
public abstract class BaseVarDef<T>
{
public string DeveloperDescription = "";
public T Value;
public void SetValue(T value)
{
Value = value;
}
public void SetValue(BaseVarDef<T> value)
{
Value = value.Value;
}
public void ApplyChange(T amount)
{
AddValue(amount);
}
public void ApplyChange(BaseVarDef<T> amount)
{
AddValue(amount.Value);
}
protected abstract void AddValue(T val);
}
public class FloatVar : BaseVarDef<float>
{
protected override void AddValue(float val)
{
Value += val;
}
}
Upvotes: 3