【问题标题】:New duplicate class but with one less attribute in C#?新的重复类,但在 C# 中少了一个属性?
【发布时间】:2020-09-14 02:19:42
【问题描述】:

假设我有一门课

public class ClientModel
{
    [Required]
    public int Id { get; set; }
    [Required]
    [MinLength(2, ErrorMessage = "First name can not be shorter than 2 characters")]
    public string FirstName { get; set; }
    [Required]
    [MinLength(2, ErrorMessage = "Last name can not be shorter than 2 characters")]
    public string LastName { get; set; }
    public string Email { get; set; }
}

我想将 ClientModel Obj 传递给函数,但没有 email 值。这样做的有效方法是什么(除了忽略函数中的电子邮件)。有没有办法在现场创建一个新的类减去我们不需要的字段?

【问题讨论】:

  • 创建另一个课程,但没有电子邮件。谷歌“c# dto 对象”stackoverflow.com/questions/14724612/…
  • 或者您可以根据需要创建一个父类,然后ClientModel 添加电子邮件。
  • 如果寻找关键字,上面将是“继承”。 C# 适用于 LSP 子类型多态性,无法“删除”字段,因为这会破坏(类型)替换,因此只能在派生类型中添加新字段,这需要切换方法。
  • 我所知道的“创建一个新的内联类型来调用一个函数”最接近的方法是传递一个ValueTuple with named items。我在这里推荐它,尽管它在某些情况下是合适的。
  • OP,我注意到您在标题中使用了“属性”一词,在问题正文中使用了“字段”。这两个术语实际上都有其他含义。这里需要的词是“财产”,至少如果您在示例中谈论的是Email

标签: c# oop inheritance


【解决方案1】:

OP 请仔细阅读XY Problem,我强烈怀疑您的问题有比您在此处要求的更好的解决方案。

类型继承

实现这一点的一种方法是通过继承,但这实际上与您期望的相反,c# 中的 child 类型无法删除 parent 类型,所以在这个解决方案中,我们将ClientModel 设为孩子!

  1. 将您当前的班级重命名为ClientModelBase
  2. 添加一个名为ClientModel 的新类,它继承自ClientModelBase
  3. 将电子邮件属性从ClientModelBase 移动到ClientModel

结果如下:

public class ClientModelBase
{
    [Required]
    public int Id { get; set; }
    [Required]
    [MinLength(2, ErrorMessage = "First name can not be shorter than 2 characters")]
    public string FirstName { get; set; }
    [Required]
    [MinLength(2, ErrorMessage = "Last name can not be shorter than 2 characters")]
    public string LastName { get; set; }
}

public class ClientModel : ClientModelBase
{
    public string Email { get; set; }
}

现在在运行时,您可以将ClientModel 的实例转换为ClientModelBase,因此对于需要ClientModel 没有 Email 属性的方法,您可以将参数类型设置为@ 987654334@.

// Get an instance of a client
ClientModel client = clientRepo.ByKey(123);
ProcessClientWithoutEmail(client);
...
public void ProcessClientWithoutEmail(ClientModelBase client)
{
    // client doesn't expose an Email property
}

这种隐式转换技术类似于C# value boxing,如果您稍后在逻辑链中需要它,Email 属性在技术上仍然可以访问,我们已经有效地对对象应用了掩码,以便编译器仅将其视为基础键入,何时或如果需要,并且只有当原始实例是 ClientModel,您才能将引用转换回具有 Email 属性的 ClientModel


接口[组成]

您也可以使用interface,而不是创建一个基类 供ClientModel 继承。然而,这需要更多的代码来实现,因为您需要完全定义接口的原型。最终结果是相似的,它对原始模型的影响较小,因为唯一的变化是声明 ClientMode 实现新的IClientModelName 接口:

public class ClientModel : IClientModelName
{
    [Required]
    public int Id { get; set; }
    [Required]
    [MinLength(2, ErrorMessage = "First name can not be shorter than 2 characters")]
    public string FirstName { get; set; }
    [Required]
    [MinLength(2, ErrorMessage = "Last name can not be shorter than 2 characters")]
    public string LastName { get; set; }
    public string Email { get; set; }
}

public interface IClientModelName
{
    int Id { get; set; }
    string FirstName { get; set; }
    string LastName { get; set; }
}

这与前面的代码示例几乎相同,但有一个重要区别,虽然仍然可以转换回 ClientModel,但我们现在可以传递与 ClientModel 来自的 repo 无关的对象,所以这简化了单元测试或模拟。

如果您的生产代码已经具有复杂的继承或其他影响初始化的因素,您可能会发现使用接口的基于组合的解决方案更适合您的用例。

// Get an instance of a client
ClientModel client = clientRepo.ByKey(123);
ProcessClientWithoutEmail(client);
...
public void ProcessClientWithoutEmail(IClientModelName client)
{
    // client doesn't expose an Email property
}

【讨论】:

    猜你喜欢
    • 2015-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-07
    • 2021-08-02
    • 1970-01-01
    • 1970-01-01
    • 2020-03-05
    相关资源
    最近更新 更多