C# 将类方法移到泛型实现中

C# 将类方法移到泛型实现中,c#,unity3d,generics,C#,Unity3d,Generics,我正在尝试重构一些代码,不得不承认我对泛型的概念还不熟悉 我有一个基本类: BaseVarDef.cs: using UnityEngine; public abstract class BaseVarDef<T> : ScriptableObject { [Multiline] public string DeveloperDescription = ""; public T Value; public void SetValue(T value

我正在尝试重构一些代码,不得不承认我对泛型的概念还不熟悉

我有一个基本类:

BaseVarDef.cs:

using UnityEngine;
public abstract class BaseVarDef<T> : ScriptableObject
{
    [Multiline]
    public string DeveloperDescription = "";

    public T Value;

    public void SetValue(T value)
    {
        Value = value;
    }

}
然后我有几个类是从这个派生出来的。但它们都包含相同的3个方法,我想将它们重构成这个通用基类。唯一的区别是它们期望当前定义它们的泛型类型或类

浮动变量

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;
    }
}
有没有办法将:SetValue和2个重载的ApplyChange方法重构为BaseVarDef.cs


提前感谢

我想您可以在基类中定义所有Apply和Set

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

我认为您可以在基类中定义所有Apply和Set

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

如果只需将这些方法添加到原始基类中,然后从中删除抽象,然后只需执行BaseVarDef或BaseVarDef。不再需要FloatVar或StringVar类

这里需要技巧的部分是+=运算符,它不能应用于泛型,至少我知道没有允许您指定实现运算符的类型的约束

因此,您可以创建一个抽象的Add方法,派生类必须按照Svetlana描述的那样实现该方法,也可以使用如下所示的动态方法,完全不需要派生类

动态的一个问题是,动态类型没有编译时检查,因此它不是万无一失的。类型T必须重载+运算符,否则将出现运行时异常。您可以通过包装行值=amt+val来避免这种情况;在一次尝试中,你可能会得到意想不到的行为

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 { }           
    }
}

如果只需将这些方法添加到原始基类中,然后从中删除抽象,然后只需执行BaseVarDef或BaseVarDef。不再需要FloatVar或StringVar类

这里需要技巧的部分是+=运算符,它不能应用于泛型,至少我知道没有允许您指定实现运算符的类型的约束

因此,您可以创建一个抽象的Add方法,派生类必须按照Svetlana描述的那样实现该方法,也可以使用如下所示的动态方法,完全不需要派生类

动态的一个问题是,动态类型没有编译时检查,因此它不是万无一失的。类型T必须重载+运算符,否则将出现运行时异常。您可以通过包装行值=amt+val来避免这种情况;在一次尝试中,你可能会得到意想不到的行为

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 { }           
    }
}

然后从BaseBarDefExt类派生:FloatVar、StringVar等?您不再需要FloadVar了……只需要BaseVarDefExt。这使您不必为每种类型定义派生类;如果类型T未实现+运算符,则将引发运行时异常。你可以用try/catch来包装它,但是Svetlana的回答以一种更安全的方式来处理这个问题——强制客户端实现AddValue方法。我重新阅读了你的问题,现在明白你想要组合这些类,所以我修改了上面的答案。然后派生:FloatVar,StringVar,BaseBarDefExt类中的etc?您不再需要FloadVar…只需要BaseBarDefExt。这使您不必为每种类型定义派生类;如果类型T未实现+运算符,则将引发运行时异常。你可以用try/catch来包装它,但是Svetlana的回答以一种更安全的方式来处理这个问题——强制客户端实现AddValue方法。我重新阅读了你的问题,现在明白你想要组合这些类,所以我修改了上面的答案。我认为这是解决这个问题的最好办法,特别是因为我想说你应该把孩子们分开。AddValue处理浮点数和字符串的方式非常不同,因此将该操作声明为抽象操作并让子类处理它是有意义的;通过这种方式,您可以在以后以完全不同的方式实现另一个AddValue类,而无需使用+=运算符。我认为这是解决此问题的最佳解决方案,特别是因为我认为您应该将子类分开。AddValue处理浮点数和字符串的方式非常不同,因此将该操作声明为抽象操作并让子类处理它是有意义的;通过这种方式,您可以在以后以完全不同的方式实现另一个AddValue类,而无需使用+=运算符。