简体   繁体   中英

C# Class Inheritance

I am working with insurance and have two different policy types - motor and household, represented by two different classes, Motor and Household.

Both have several bits of data in common, so both would inherit from another class called Policy. When a user logs into the app, they could have either a motor or a household policy, so the app needs to display the generic information and the information unique to Motor or Household. To encapsulate all this, i have a response object that has both a Motor member and a Household member, as shown below:

public class Response
{
    ...
    private MotorPolicy                 _motorPolicy;
    private HouseholdPolicy             _householdPolicy;
    ....
}

The code below should demonstrate:

if (response.PolicyType == Enumerations.PolicyType.Motor) 
{
    lblDescription.Text = response.MotorPolicy.Description;
    lblReg.Text = response.MotorPolicy.Reg;
}
else
{
    lblDescription.Text = response.HouseholdPolicy.Description;
    lblContents.Text = response.HouseholdPolicy.Contents;
}

The MotorPolicy doesn't have Contents property and the HouseholdPolicy doesn't have a Reg property.

But I really want to simply do:

if (response.PolicyType == Enumerations.PolicyType.Motor) 
{
    lblDescription.Text = response.Policy.Description;
    ...
}

I have tried using generics, could couldn't find the right solution.

Your response only needs a Policy type, you can then store a MotorPolicy or HouseholdPolicy type into it.

Then your response just needs to check for data type

if (response.Policy is MotorPolicy) ....

Alternatively have an abstract method or a property returning data from an abstract method on the Policy type that is fully inplemented by the child classes and returns reg data or contents data as apporpriate.

Each Policy descendant (now you have two, you might have more in the future, right?) should have their own UI controls which "know" how to deal with the policy information. The same approach can be used for other things, such as a "controller" for policy objects etc.

The response can then be made generic:

public class Response<T> where T: Policy {
    ...
    private T _policy;
    ....
}

Alternatively, you could have a more generic approach which uses reflection to display the information, but those are usually less "sexy" in their appearance and usability (think of the Property Grid in the VS designer).

public interface IPolicy
{
    string Description { get; }
    string Reg { get; }
    string Contents { get; }
}

public class MotorPolicy : IPolicy
{
    public string Description
    {
        get { return ...; }
    }

    public string Reg
    {
        get { return ...; }
    }

    public string Contents
    {
        get { return String.Empty; }
    }
}

public class HousholdPolicy : IPolicy
{
    public string Description
    {
        get { return ...; }
    }

    public string Reg
    {
        get { return String.Empty; }
    }

    public string Contents
    {
        get { return ...; }
    }
}

public class Response
{
    ...
    private IPolicy             _policy;
    ....
}

Now you don't need an Enumeration to show which type you've implemented, you can just say

lblDescription.Text = response.Policy.Description;
lblReg.Text = response.Policy.Reg;
lblContents.Text = response.Policy.Contents;

Edit: Alternate solution

public interface IPolicy
{
    string Description { get; }
}

public interface IHasReg
{
    string Reg { get; }
}

public interface IHasContents
{
    string Contents { get; }
}

public class MotorPolicy : IPolicy, IHasReg
{
    public string Description
    {
        get { return ...; }
    }

    public string Reg
    {
        get { return ...; }
    }
}

public class HouseholdPolicy : IPolicy, IHasContents
{
    public string Description
    {
        get { return ...; }
    }

    public string Contents
    {
        get { return ...; }
    }
}

public class Response
{
    ...
    private IPolicy             _policy;
    ....
}

This leaves you with more code in the calling function

lblDescription.Text = response.Policy.Description;
IHasReg hasReg = response.Policy as IHasReg;
if (hasReg != null) lblReg.Text = hasReg.Reg;
IHasContents hasContents = response.Policy as IHasContents;
if (hasContents != null) lblContents.Text = hasContents.Contents;

but is considerably more extensible than other options presented and complies with your desire to avoid functionality in the implementation which doesn't make sense.

One option is to add a member to Policy that synthesizes all the derived class' relevant properties to provide a summary:

 public abstract class Policy {
     public string Description { get; set; }
     public abstract string Summary { get; }
 }

 public class MotorPolicy: Policy {
     public override string Summary {
         get { return this.Description + "\r\n" + this.Reg; }
     }
 }

 public class HouseholdPolicy: Policy {
     public override string Summary {
         get { return this.Description + "\r\n" + this.Contents; }
     }
 }

This centralizes the logic and makes the user interface code simple:

 label.Description.Text = response.Policy.Summary;

That basic implementation sacrifices the ability to format the subsections separately. You could overcome that by exposing the summary as a collection of strings:

public abstract IEnumerable<string> SummarySections { get; }

If you want to display the derived classes' details in fundamentally different ways, you'll have to embrace the conditional logic in the user interface layer (for example, you might list the household policy's contents in a table, but show a scanned image for the motor policy's registration).

Use the template pattern:

Create a base class called Policy with a virtual abstract get method to determine the description of the policy.

public abstract class Policy
{ 
    protected virtual string GetDescription()
    {
         return string.Empty()    
    }

    public string Description 
    { 
        get 
        {
           return GetDescription();
        } 
    }
}

public MotorPolicy : Policy
{
    public override string GetDescription()
    {
       return ..... ////specific description implementation for MotorPolicy
    }
}

public HouseHoldPolicy : Policy
{
    public override string GetDescription()
    {
       return ..... ////specific description implementation for HouseholdPolicy
    }
}


public class Response        
{        
    ...        
    private MotorPolicy                 _motorPolicy;        
    private HouseholdPolicy             _householdPolicy; 
    private PolicyType                  _policyType;       
    ....        

    public Policy Policy
    {
        get
        {
           if (_policyType== PolicyType.Motor) 
           {
              return _motorPolicy;
           } 
           if (_policyType== PolicyType.Household) 
           {
              return _householdPolicy;
           } 

           return null;
        }
    }        
}    

client code:

if (response.Policy != null)        
{       
    lblDescription.Text = response.Policy.Description;       
    ...       
}    

Let MotorPolicy and HouseholdPolicy derive from Policy and override the abstract get method from the base and create a specific implementation of it.

In the Response class just get the description.

define the Policy interface and implement it in your both the policy classes

Interface IPolicy{
    int Reg {get;set;};
    string Contents {get;set;};
}

MotorPolicy : Policy,IPolicy {

 string IPolicy.Contents 
     {get;set;};


 int IPolicy.Reg 
     {get;set;};

}

HouseholdPolicy : Policy , IPolicy {
 string IPolicy.Contents 
     {get;set;};


 int IPolicy.Reg 
     {get;set;};
}

最简单的解决方案是实现具有description属性和“contents”属性的接口,然后在您的motor策略类中创建一个返回“reg”的虚拟“contents”属性。

Can your response contain either a MotorPolicy or a HouseholdPolicy or, can it contain one of each?

If you are dealing with one or the other then create a base type that both classes inherit that defines the common properties. When you output the common properties just cast the Policy as the base type and use that.

My immediate thought is to go for:

public abstract class Response
{
  public abstract Policy Policy {get;}//can be used for stuff for dealing with all policies.
  public static Response GetResponse(Policy policy)
  {//factory method
    if(policy is MotorPolicy)
      return new MotorResponse((MotorPolicy)policy);
    if(policy is HouseholdPolicy)
      return new HouseholdResponse((HouseholdPolicy)policy);
    throw new ArgumentException("Unexpected policy type");
  }
}
public class MotorResponse : Response
{
  private readonly MotorPolicy _motorPolicy;
  public MotorResponse(MotorPolicy policy)
  {
    _motorPolicy = policy;
  }
  protected override Policy Policy
  {
    get { return _motorPolicy; }
  }
  // motor specific stuff
}
public class HouseholdResponse : Response
{
  private readonly HouseholdPolicy _householdPolicy;
  public HouseholdResponse(HouseholdPolicy policy)
  {
    _householdPolicy = policy;
  }
  protected override Policy Policy
  {
    get { return _householdPolicy; }
  }
  // household specific stuff
}

I would try something like this:

  public class Response
  {
     public Policy SelectedPolicy {get;set;}

     //I don't think you need these, but hard to 
     //say without seeing the rest of the code
     ...
     private MotorPolicy                 _motorPolicy;
     private HouseholdPolicy             _householdPolicy;
     ....
  }

then

lblDescription.Text = response.SelectedPolicy.Description;

if (SelectedPolicy is MotorPolicy)
    lblReg.Text = ((MotorPolicy)response.SelectedPolicy).Reg;

else if (SelectedPolicy is HouseholdPolicy)
    lblContents.Text = ((HouseholdPolicy)response.SelectedPolicy).Contents;

I would not put both Reg and Contents in the base class or interface. If I do what's the purpose of inheritance if all classes look the same? The only benefits I would get would be types, and that's not going to gain me much in this case.

maybe I don't understand the question but I would just use inheritence

define policy as

public class Policy { public string Description{ get; set;} public string Details {get; set;}

}

public class MotorPolicy:Policy 
{
    public void SetReg(string reg)
    {
        base.Details = reg;
    }
}

public class HousePolicy:Policy 
{
    public void SetContents(string contents)
    {
        base.Details = contents;
    }
}

and call by

    private void Form1_Load(object sender, EventArgs e)
    {
        MotorPolicy mp = new MotorPolicy();
        mp.Description = "Motor";
        SetForm(mp);       
    }

    private void SetForm(Policy p)
    {
        lblDescription.Text = p.Description;
        lblDetail.Text = p.Details;

        //then if you still need specifics 
        if (p.GetType() == typeof(MotorPolicy))
        {
            MotorPolicy mp = p as MotorPolicy;
            //continue assigning mp
        }
        else if (p.GetType() == typeof(HousePolicy))
        {
            HousePolicy hp = p as HousePolicy;
            //continue assigning Hp
        }
    }

Note I put reg/contents as a field detail as they are both string types. If one was int vs string then they would have to be done separate.

Yours is a unique example of "Refactoring condition to Polymorphism" [Fowler].

And then your method should accept the proper object and do as below:

public void Update(IPolicy policy)
{
        lblDescription.Text = policy.Description;
        lblReg.Text = .Reg;

}

Well, I dislike abstract classes so I went with an interface for Policy

public interface IPolicy
{
    string Description { get; set;}
    void Display();
}

Then we inherit from it to create MotorPolicy

public class MotorPolicy : IPolicy
{
    public string Description { get; set; }
    public string Reg { get; set; }

    public void Display()
    {
        Console.WriteLine(string.Format("Description: {0}", Description));
        Console.WriteLine(string.Format("Reg: {0}", Reg));
    }
}

Then for response I changed the Policy to a List in the off chance that you can have both or either. Now we've offloaded the handling of displaying the data to the specific policy itself.

public class Response
{
    public List<IPolicy> Policies { get; set; }

    public void Display()
    {
        Policies.ForEach(p => p.Display());
    }

    public void Display(Type t)
    {
        var policy = (from p in Policies
                      where p.GetType() == t
                      select p).FirstOrDefault();
        policy.Display();
    }
}

This could easily be changed to not use the List and we can get rid of the overloaded Display.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM