使用安全向下转换的类设计选项

时间:2011-09-07 01:24:19

标签: c# entity-framework asp.net-mvc-3 inheritance casting

在倾向于向下倾斜(见[my original post])并制作[deep copies]后,我发现[this eponymous article]在C ++中提出了如何处理问题的建议。我兴奋地用C#实现了如下:

public partial class User
{
virtual public Employer GetEmployer() { return null; }
...
}

public partial class Employer
{
public override Employer GetEmployer() { return this; }
...
}

然后我就这样使用了:

User u = GetUser();
Employer e = u.GetEmployer();

然而(我想,毫不奇怪),永远不会调用覆盖并返回null。


我试图解决的问题是我收集的一个非常常见的用例:我得到一些我需要存储的数据,但它不完整。后来我获得了更多的数据,并用它来改进(低估)我对这个世界的理解。

在这种特殊情况下,我从使用我网站的人那里得到一个电子邮件地址,所以我知道他们是User,但总的来说我对此不了解。之后(当他们填写表格时),我知道他们实际上是Employer,所以我需要低估我的User

这里的正确方法是什么?

3 个答案:

答案 0 :(得分:1)

我回答了你以前的一个问题。你正试图解决一些无法解决的问题。这里的解决方案是不使用继承。为什么因为继承是is a关系。所以如果你有:

public class User { }

public class Employee : User { }

您已此关系Employeee is User您没有反向关系User is Employee。但是,从UserEmployee进行投射,您要做的就是这样做。 User的实例无法转换为Employee(除了我之前回答中提到的情况 - 但您没有这种情况)。

使用这种方法,你将以面向对象的方式解决它,而不需要继承转换或其他任何东西。

public class User 
{
    public virtual EmployeeData { get; set; }
}

public class EmployeeData { }

该方法会将您的设计从is a更改为has a关系。在这种情况下,EmployeeData是以1 - 0..1关系映射的单独实体(这将导致数据库中的两个表)。如果您对UserEmployeeData都存储在同一个表中的事实感到满意,也可以使用1 - 1关系或ComplexType。

答案 1 :(得分:0)

您的示例中唯一缺少的部分是雇主不会从用户继承。

将您的声明更改为:

public partial class Employer : User
{
    // ...
}

你应该好好去。我不确定我会走这条路。我可能只是使用as关键字来安全地进行投射:

var user = GetUser();
var employer = user as Employer;

// if the cast failed, employer will be null
if(employer != null)
{
    // Work with employer
}

答案 2 :(得分:0)

您应该关注State Pattern

  

这是一种对象在运行时部分更改其类型的简洁方法。

然后,您可以与Prototype Pattern混合搭配:

  

[使用]一个原型实例,克隆它以生成新对象。

你可以得到这样的东西:

// State pattern: public "wrapper"
public class User {
    UserState state = UserState.CreateUser();

    public void SetIsEmployer ()
    {
        // Use UserState.IsEmployer() method to transition to the
        // Employer state
        state = state.IsEmployer ();
    }

    public User Employer {
        get {return state.Employer.User;}
    }
}

// State pattern: User state
internal class UserState {
    // protected so that only CreateUser() can create instances.
    protected UserState ()
    {
    }

    public User User {
        get {/* TODO */}
    }

    public virtual UserState Employer {
        get {return null;}
    }

    // Creates a default prototype instance
    public static UserState CreateUser ()
    {
         return new UserState ();
    }

    // Prototype-ish method; creates an EmployerState instance
    public virtual UserState IsEmployer ()
    {
        return new EmployerState (/* data to copy...*/);
    }
}

// State pattern: Employer state
class EmployerState : UserState {
    internal EmployeeState ()
    {
    }

    public override UserState Employer {
        get {return this;}
    }
}

如果您的公共User类型需要更多“状态转换”,则只需在UserState类型上提供更多类(每个状态一个)和prototype-ish方法以创建适当的目标状态类型。 User.state始终是User的当前状态。此设置允许您在运行时更改实例的表观运行时类型。