【问题标题】:Polymorphism: getting it right多态性:正确
【发布时间】:2012-04-20 00:20:24
【问题描述】:

我试图弄清楚 C# 中的多态/继承情况。

我现在拥有的是这些课程:

Lease (the base class containing the general data)
PrivateLease (inheriting from the Lease class)
BusinessLease (inheriting from the Lease class)

我想要实现的是:

Lease lease = new PrivateLease();

这目前有效,但在执行此操作时我无法访问 PrivateLease 对象上的属性。至少要先将Lease 对象转换为PrivateLease 对象。

我希望 Lease 对象成为 PrivateLeaseBusinessLease 对象的通用对象,该对象包含其中一个对象的所有数据。然后在向数据库插入/更新/删除时,我会先询问它是哪种类型,以确定将数据插入到哪些表中。

我有一种奇怪的感觉,上面的方法不是解决这个问题的正确方法。有人对此有任何提示吗? :-) 我在谷歌上搜索并阅读了我的编程书籍,每个人都建议使用这种方法,即拥有一个基类,然后从它继承到其他类。

非常感谢任何帮助/提示!

提前致谢。

编辑

应该从一开始就详细说明一下,我很抱歉!

上面提到的类只是从我的 ASP.NET 解决方案的 UI 中保存数据,以通过数据访问层对数据库执行 CRUD 操作。所以基本上这些类只包含一堆保存数据的属性。即:

public class Lease
{
    public int Id { get; set; }
    public bool IsActive { get; set; }
    public string TypeOfRental { get; set; }
    public string RentalPeriod { get; set; }
    public DateTime TakeoverDate { get; set; }        
}

public class PrivateLease : Lease
{
    public string Floor { get; set; }
    public string Side { get; set; }
    public int FloorSize { get; set; }
    public int NumberOfRooms { get; set; }
}

等等。

PrivateLeaseBusinessLease 类是不同的,因为现实世界中存在不同的租赁变量 :-)

基本上我可以只使用两个单独的PrivateLeaseBusinessLease 对象,但由于模型规定Address 对象可以容纳一个或多个Leases,所以这不是一个选项。

对我来说,我似乎将在 ASP.NET 前端和 DAL 上经历一次重大的铸造地狱? :-/

【问题讨论】:

标签: c# .net inheritance polymorphism


【解决方案1】:

不要在消费者层决定(选择逻辑),而是让类自己决定:

// or you ILease interface if a parent class will not contain any shared logic
abstract class Lease
{
    public abstract void Do();

    // example of shared logic
    protected void Save(Lease l) { }
}

class PrivateLease : Lease
{
    public override void Do() { // private logic here }
}

class BusinessLease : Lease
{
    public override void Do() { // business logic here }
}

用法:

Lease l = ...
l.Do(); // execute the logic

您可能想要创建一个工厂来创建对象:

static class LeaseFactory<T> where T : Lease, new() // constraint to require default constructor existence
{
    public static Leas Create()
    {
        return new T();
    }
}

【讨论】:

  • 有点不幸将方法命名为'Lease' :)
  • @NSGaga:同意。重命名为 Do()
  • 嗨 abatishchev,非常感谢您的帮助 :-) 问题是我的类实际上并没有包含任何逻辑,它们只是包含一堆保存数据的属性(我想你可以将它们视为 DTO,真的),因此在这种情况下将没有实际覆盖的方法 :-)
  • @bomortensen:“仅属性容器”通常称为 POCO,您已经可以遇到这样的缩写。通常在 MVC 中,有 2 种方法来扩展逻辑:瘦模型和厚模型。第一个仅包含数据(纯 POCO),第二个也包含逻辑。我认为您的情况适合第二种方式。像LeaseRepository.Do() 这样的东西会接受Lease 并调用Do()
  • 您好,abatischev。我还没有真正深入研究 POCO 是什么,但在我看来,第一个(瘦模型)似乎最适合我的需求,因为我的课程中根本没有任何逻辑。它们仅包含用于在 UI 和 DAL 之间保存/传输数据的属性。
【解决方案2】:

你是正确的拥有一个基类的基本方法。

您需要做的是将任何common 属性放在基类中。那么如果你有不同的业务规则,可以用虚函数来实现,被多态调用。

abstract class Lease
{
  public int MonthlyCost {get;set;}

  public string CustomerName {get;set;}

  // Declare that all Leases have to have an IncreaseCost method.
  public abstract void IncreaseCost();
}

class PrivateLease : Lease
{
  // Private leases are incremented by an absolute number (10).
  public override void IncreaseCost()
  {
    MonthlyCost += 10;
  }
}

class BusinessLease : Lease
{
  // Business leases are incremented by 10%.
  public override void IncreaseCost()
  {
    MonthlyCost *= 1.10;
  }
}

// Somewhere in your code...
Lease lease = new PrivateLease();

// This call is polymorphic. It will use the actual type of the lease object.
lease.IncreaseCost();

【讨论】:

  • 嗨 Anders,非常感谢您的意见 :-) 所以基本上我必须将 PrivateLease 和 BusinessLease 上的属性声明为公共虚拟字符串 MyProperty { get;放; } ?
  • 如果它只是一个简单的属性,你不应该让它virtual。创建属性virtual 很少有任何好处。另一方面,方法通常值得virtual
  • @AndersAbel - PrivateLeaseBusinessLease 应该在覆盖 IncreaseCost 之前从 Lease 继承;)
  • @MetroSmurf:当然,我已经编辑了我的答案。有趣的是,两个月内没有其他人看到它......谢谢!
【解决方案3】:

在现代 OOD 中,对于这种情况,您可以使用 interfaces

编辑:

在我看来,为了避免强制转换,您可以有多个接口用于多种用途。那么PrivateLeaseBusinessLease 可以实现相应的。

interface IWrite
{
    string Data { get; set; }
    void Write();
}

interface IRead
{
    string Data { get; set; }
    void Read();
}

public class Lease
{
    //..
}

public class PrivateLease : Lease, IWrite, IRead
{
    // other implementations
    public string Data { get; set; }
    public void Read()
    {
        //..
    }
    public void Write()
    {
        //..
    }
}
public class BusinessLease : Lease, IRead
{
    // other implementations
    public string Data { get; set; }
    public void Read()
    {
        //..
    }
}

【讨论】:

  • 嗨 Mouliyan,感谢您的帮助 :-) 不过,如果我这样做:ILease lease = new PrivateLease();如果 PrivateLease 对象上的属性不是通过 ILease 接口实现的,我将无法访问它们,我对此不感兴趣,因为 Lease 类应该包含 Lease 类型对象的所有常规数据..跨度>
  • 可以为相关属性实现不同的接口。
【解决方案4】:

在 Lease 类中添加名为 DBUpdate 的虚拟方法,并在两个派生类中覆盖它。

假设某个 Utility 类有 LeaseDBOperation 方法如下所示:

public static void LeaseDBOperation (Lease anylease)
{

  anyleaase.DBUpdate();
}

您可以将此方法称为:

var pl = new PrivateLease();
..set all the properties of **pl**
//call this for db operations  :
Utility.LeaseDBOperation(pl)

这里在 LeaseDBOperation 方法中,如果根据类型 send ,将调用所需类的 DBUpdate 方法。

【讨论】:

  • 嗨 Dhananjay,感谢您的意见 :-) 这或多或少是我现在的做法。在我的 DAL 上有一个 CreateLease(租赁租约),我在数据库中创建租约。我对此的主要“问题”是,在将任何内容插入数据库之前,我必须检查租赁对象是 PrivateLease 还是 BusinessLease..
  • 其实不是。 anyleaase.DBUpdate() 方法会小心处理。 DBUpdate() 方法被写入两个租赁类(私人和商业租赁。)。您不必担心要更新哪一个..
【解决方案5】:
Lease l = (Lease)sth;

if (l is PrivateLease)
{
    PrivateLease p = (PrivateLease)l;    
    //do private logic here
}
else if (l if BussinessLease)
{    
    BussinessLease b = (BunessinessLease)l;
    //do bussiness logic here
}

【讨论】:

猜你喜欢
  • 2011-08-05
  • 2020-10-09
  • 1970-01-01
  • 1970-01-01
  • 2020-03-01
  • 2012-11-20
  • 2017-12-09
  • 2013-10-03
  • 1970-01-01
相关资源
最近更新 更多