c# - 为特定用例扩展基础模型类

标签 c# oop

我们有一个应用程序,总体上被许多不同的客户用作“通用”产品,通常具有可供所有客户使用的相同功能。但是,有一些单独定制的组件,尤其是与从客户各自的内部系统导入数据有关的组件。

到目前为止,这主要意味着将不同的输入格式转换为相同的数据结构,但为某些类型保留额外的客户特定数据并在同样“了解”这一点的选定代码部分再次使用它变得越来越重要特定客户。在整个应用程序中,通常不会有很多不同的地方。

为了在不“污染”核心模型类的情况下做到这一点,“可扩展性”目前是通过让相关类派生自 DynamicObject 来实现的。然后用一些代码来动态添加用魔法字符串键入的属性,并在其他地方检查这些属性并检索它们,这甚至不是类型安全的,因为您需要知道将可能的值转换为什么。

我对此很不高兴,并想提出一个解决方案,感觉有点不像“猴子补丁”,而且在静态类型环境中更符合习惯。问题是实现这一目标的最佳选择是什么。 (附加要求是附加数据应该易于与对象的其余部分序列化/反序列化。)

  • 面向对象语言中“显而易见”的事情当然是对模型类型进行子类化,但这会在对象创建和复制方面产生许多新问题,而且通常我不太喜欢(而且它只是违反了“继承之上的组合”)。
  • 仍然使用继承但限制其影响的解决方案是添加单个属性 ExtensionData到实际的类,它的类型是空的 *ExtensionData然后根据客户进行子分类的类。使用它确实涉及对实际扩展数据对象的一些类型检查和转换,但除此之外,它将是完全类型安全的。我认为这是我现在首选的方式。
  • 与目前使用的动态方法非常相似的是用 Dictionary<string, object> 替换先前变体中的类型化对象。 .我认为这仍然比现在发生的情况要好,但它具有相同的基本问题,即动态类型化和使用魔术字符串(当然,即使它们将存储为常量)。

扩展数据实际上不是动态的;一旦知道要导入哪些客户数据,我们就处于一个定义明确的子域中,对导入的数据中的内容并不感到意外。

(通常情况下,这在 F# 中会更容易,我可以使用范围扩展属性或将 ExtensionData 作为 DU,其中每个案例都包含一个客户特定的记录类型,该记录类型将通过模式明确标识匹配。)

在 C# 中执行此操作的最“惯用”且可维护的方法是什么?是否有任何提到的替代方案具有我没有考虑过的巨大优势或缺陷?

最佳答案

你可以使用类似的东西

public class BaseClass
{
    Dictionary<Type, object> propertys = new Dictionary<Type, object>();

    public void Add<T>(T instance)
    {
        propertys[typeof(T)] = instance;
    }

    public T Get<T>()
    {
        return (T)propertys[typeof(T)];
    }

    public void Test()
    {
        Add<string>("Hello World");

        string helloWorld = Get<string>();
    }
}

限制是您只能为给定类型添加一个实例/属性。

如果性能不是问题,另一个解决方案可能如下所示。 但是请注意,使用这样的表达式很慢(呃)。你给它一个 lambda,但是隐式转换为 Expression 需要一些时间

using System.Linq.Expressions;

// There will never be an implementation of this interface
// Its only there to "define" the property names and types without
// magic strings and in a type safe way
public interface IBaseClassExtension
{
    public string ExtensionProperty { get; set; }
    public int ExtensionProperty2 { get; set; }
}

public class BaseClass
{
    Dictionary<string, object> propertys = new Dictionary<string, object>();

    public void Add<T, U>(Expression<Func<T, U>> expr, U instance)
    {
        var propExpr = expr.Body as MemberExpression;
        // The declaring types name could be used in addition to be sure there 
        // is no naming conflict with members of other types that have the same member name
        //string name = propExpr.Member.DeclaringType.FullName + propExpr.Member.Name;
        propertys[propExpr.Member.Name] = instance;
    }

    public U Get<T, U>(Expression<Func<T, U>> expr)
    {
        var propExpr = expr.Body as MemberExpression;
        //string name = propExpr.Member.DeclaringType.FullName + propExpr.Member.Name;
        return (U)propertys[propExpr.Member.Name];
    }

    public void Test()
    {
        Add<IBaseClassExtension, string>(bce => bce.ExtensionProperty, "Hello World");
        string helloWorld = Get<IBaseClassExtension, string>(bce => bce.ExtensionProperty);
    }
}

这两种解决方案都可以以类型安全的方式使用,即使字典也只知道对象

如果您有时间等待 C# 6.0,新的 nameof 运算符应该比使用表达式快得多。但是你会失去一些类型安全性。

public interface IBaseClassExtension
{
    public string ExtensionProperty { get; set; }
    public int ExtensionProperty2 { get; set; }
}

public class BaseClass
{
    Dictionary<string, object> propertys = new Dictionary<string, object>();

    public void Add(string name, object instance)
    {
        propertys[name] = instance;
    }

    public T Get<T>(string name)
    {
        return (T)propertys[name];
    }

    public void Test()
    {
        Add(nameof(IBaseClassExtension.ExtensionProperty), "HelloWorld");
        string helloWorld = Get<string>(nameof(IBaseClassExtension.ExtensionProperty));
    }
}

编辑:

如果扩展属性/数据的数量不是那么多,另一种解决方案可能是使基本模型通用

public class BaseClass
{
}

public class BaseClass<T, U, V> : BaseClass
{
    public T Extension1 { get; set; }
    public U Extension2 { get; set; }
    public V Extension3 { get; set; }
}

public class ClassThatIsUsingSpecificBaseClass
{
    public void Test(BaseClass<int, int, string> baseClass)
    {
        //baseClass.Extension1 ...;
        //baseClass.Extension2 ...;
    }
}

public class ClassThatIsUsingBaseClass
{
    public void Test(BaseClass baseClass)
    {
    }
}

这将是类型安全的,不涉及强制转换,并且只会派生一个 BaseClass,而不是针对每个独特的客户需求/数据。

关于c# - 为特定用例扩展基础模型类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30043319/

相关文章:

oop - 策略模式是对的吗?

design-patterns - 控制反转是否特定于 OO 语言?

c# - 将 c# asp.net web 应用程序与 C++ dll 链接时,dll 代码是在服务器上运行还是在客户端上运行?

javascript - 成功提交后进行验证

c# - NHibernate JoinQueryOver 条件

c++ - 虚函数与指针转换的比较

c# - 如何使用 asp.net 在母版页中编写内部 jquery 代码?

c# - 在默认构造函数或内联中​​初始化变量之间有区别吗?

C的代码组织风格?

javascript - 在 Javascript 中扩展对象