How can I perform a type-based lookup?

In my game, I have properties that represent values associated with characters. This could be, for instance, a player’s health, their stamina, energy and so on. These values can be modified through external sources, for instance through buffs, talents, curses, passive abilities and so on.

I use SO instances to define what properties exist instead of using a C# enumeration:

public abstract class PropertyData<T> : ScriptableObject
{
    [SerializeField]
    private T defaultValue = default;

    [SerializeField]
    private int identifier = -1;

    public T DefaultValue => defaultValue;

    public int Identifier => identifier;
}

Different types of properties

[CreateAssetMenu]
public class IntegerPropertyData : PropertyData<int> {}
[CreateAssetMenu]
public class FloatPropertyData : PropertyData<float> {}
[CreateAssetMenu]
public class BooleanPropertyData : PropertyData<bool> { }

and so on, so far only primitive types.

Each character in my game has property providers which keep track of all properties that a character currently has:

[Serializable]
public class Property<T>
{
    [SerializeField]
    private T value = default;

    [SerializeField]
    private PropertyData<T> data;

    public T Value => value;

    public int Identifier => data.Identifier;
}
public abstract class PropertyProvider<T> : MonoBehaviour
{
    [SerializeField]
    private Property<T>[] properties = null;

    public Property<T> Get(int identifier)
    {
        Property<T> property = null;

        for (int index = 0; index < properties.Length; index++)
        {
            if(properties[index].Identifier == identifier)
            {
                property = properties[index];
            }
        }

        return property;
    }
}

As well as different types here

public class FloatPropertyProvider : PropertyProvider<float> {}

and so on.

So far, so good. Now to come to my question, when I have external objects that somehow work with these properties, how can I determinate the type of a property and locate the correct property provider?

If you imagine that PropertyData<T> inherits from a non-generic PropertyData and objects only reference an object of type PropertyData, don’t I lose the type information? Something like this:

public class PropertyModifierData : ScriptableObject
{
    [SerializeField]
    private PropertyData targetProperty;
}

Is there a design pattern to resolve this issue or some other neat advice on how to deal with this issue? Do I have to make all my objects interacting with properties generic, too?

// data for a modifier that modifies a property of type T.
public abstract class PropertyModifierData<T> : ScriptableObject
{
    [SerializeField]
    private PropertyData<T> targetProperty;
}
public class FloatPropertyModifierData : PropertyModifierData<float> {}

I ended up storing all properties of all types in a single provider object, using the [SerializeReference] attribute that we got in one of the newer versions of Unity:

[Serializable]
public abstract class Property {}

[Serializable]
public class Property<T> : Property
{
    [SerializeField]
    private T value;
}

[Serializable]
public class FloatProperty : Property<float> { }

[Serializable]
public class BooleanProperty : Property<bool> { }
public class PropertyProvider : MonoBehaviour
{
    [SerializeReference]
    private Property[] properties;

    private void OnValidate()
    {
        if (properties == null || properties.Length == 0)
        {
            Debug.Log("Validating");
            properties = new Property[2];
            properties[0] = new FloatProperty();
            properties[1] = new BooleanProperty();
            Debug.Log("Validated");
        }
    }
}

I don’t like that I have to typecast my properties now when accessing them through the provider of a character, but alas, if I want all properties to be in one place, this seems to be an easy way to do it.

Are there better solutions? What about giving the provider multiple dictionaries or other look-up structures and somehow dispatch using a generic mehtod or something along the lines, allowing to keep the type information of the property while also having all properties of all types in one place?