Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/wcf/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 多态问题#_C#_Polymorphism - Fatal编程技术网

C# 多态问题#

C# 多态问题#,c#,polymorphism,C#,Polymorphism,我有一个包含用户的DB表,有些是“代理”,有些是“客户”。在我的C#项目中,我有一个用户超类和一个代理和客户端子类。代理和客户端扩展用户 在将用户对象强制转换或更改为代理或客户端对象时,我遇到了一些基本问题。我真的不知道为什么。这可能是很基本的,但我不知道怎么了 public class User { public int UserId { get; set; } public string UserType { get; set; } public DateTime D

我有一个包含用户的DB表,有些是“代理”,有些是“客户”。在我的C#项目中,我有一个用户超类和一个代理和客户端子类。代理和客户端扩展用户

在将用户对象强制转换或更改为代理或客户端对象时,我遇到了一些基本问题。我真的不知道为什么。这可能是很基本的,但我不知道怎么了

public class User
{
    public int UserId { get; set; }
    public string UserType { get; set; }
    public DateTime DateCreated { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public string Name { get; set; }
    public string Phone { get; set; }

    public User()
    {
    }
}


现在为什么我不能这样做:

public User GetUser(int userid)
    {
        User user = new User();
        User returnuser = user;
        string sql = "SELECT usertype, datecreated, email, name, phone, country, ip, company, companyreg, securityquestion, securityanswer, description, accountbalance, aothcredits, wantsrequests FROM ruser WHERE userid=@userid";
        try
        {
            using (SqlConnection con = new SqlConnection(constr))
            using (SqlCommand cmd = new SqlCommand(sql))
            {
                con.Open();
                cmd.Connection = con;
                cmd.Parameters.Add("@userid", System.Data.SqlDbType.Int).Value = userid;
                using (SqlDataReader rdr = cmd.ExecuteReader())
                {
                    if (rdr.Read())
                    {
                        user.UserId = userid;
                        user.UserType = rdr["usertype"].ToString();
                        user.DateCreated = (DateTime)rdr["datecreated"];
                        user.Email = rdr["email"].ToString();
                        user.Name = rdr["name"].ToString();
                        user.Phone = rdr["phone"].ToString();

                        string type = rdr.GetString(0);
                        if (type == "agent")
                        {
                            Agent agent = user as Agent;
                            agent.Company = rdr["company"].ToString();
                            agent.CompanyReg = rdr["companyreg"].ToString();
                            agent.SecurityQuestion = rdr["securityQuestion"].ToString();
                            agent.SecurityAnswer = rdr["securityanswer"].ToString();
                            agent.Description = rdr["description"].ToString();
                            agent.AccountBalance = (int)rdr["accountbalance"];
                            agent.WantsRequests = Convert.ToBoolean(rdr["wantsrequests"]);
                            returnuser = agent;
                        }
                        else //type == "client"
                        {
                            Client client = user as Client;
                            client.Country = rdr["country"].ToString();
                            client.IP = rdr["ip"].ToString();
                            returnuser = client;
                        }
                    }
                }
            }
        }
        catch (SqlException e)
        {
            throw e;
        }
        return returnuser;
    }

如果将对象实例化为基类,则无法从基类强制转换为子类

您正试图使用
as
用户
转换为
客户
代理
,具体取决于您的数据。但是,您在函数的开头明确地创建了一个
User
对象:

User user = new User();
此对象属于
User
类型,因此
as
将无法将其转换为
客户端
代理
,并将返回null。请参阅文档

as运算符类似于强制转换,只是在转换失败时生成null,而不是引发异常

您可以通过以下方式演示这一点:

User u = new User();
System.Console.WriteLine("u is User: " + (u is User));
System.Console.WriteLine("u is Agent: " + (u is Agent));
System.Console.WriteLine("u is Client: " + (u is Client));
// Should produce:
// u is User: true
// u is Agent: false
// u is Client: false

Agent a = new Agent();
u = a;
System.Console.WriteLine("u is User: " + (u is User));
System.Console.WriteLine("u is Agent: " + (u is Agent));
System.Console.WriteLine("u is Client: " + (u is Agent));
// Should produce:
// u is User: true
// u is Agent: true
// u is Client: false
您需要做的是显式地创建您需要的最具体的类,或者是一个新的
代理
或者
客户端
,然后在需要时将其转换为更通用的
用户

例如:

public User GetUser(int userid)
    {
        User user;
        string sql = "...";
        try
        {
            using (SqlConnection con = new SqlConnection(constr))
            using (SqlCommand cmd = new SqlCommand(sql))
            {
                //.. Snip sql stuff ... //

                        string type = rdr.GetString(0);
                        if (type == "agent")
                        {
                            Agent agent = new Agent();
                            agent.Company = rdr["company"].ToString();
                            agent.CompanyReg = rdr["companyreg"].ToString();
                            agent.SecurityQuestion = rdr["securityQuestion"].ToString();
                            agent.SecurityAnswer = rdr["securityanswer"].ToString();
                            agent.Description = rdr["description"].ToString();
                            agent.AccountBalance = (int)rdr["accountbalance"];
                            agent.WantsRequests = Convert.ToBoolean(rdr["wantsrequests"]);
                            user = agent;
                        }
                        else //type == "client"
                        {
                            Client client = new Client();
                            client.Country = rdr["country"].ToString();
                            client.IP = rdr["ip"].ToString();
                            user= client;
                        }

                        // Now do generic things
                        user.UserId = userid;
                        user.UserType = rdr["usertype"].ToString();
                        user.DateCreated = (DateTime)rdr["datecreated"];
                        user.Email = rdr["email"].ToString();
                        user.Name = rdr["name"].ToString();
                        user.Phone = rdr["phone"].ToString();

                        return user;
                    }
                }
            }
        }
        catch (SqlException e)
        {
            throw e;
        }
    }

因为是你用这条线创造的

User user = new User();
它以后不能神奇地变形为它的子类(代理)。您需要它来创建应该的类型

你应该做的是在开始的时候

if (type == "agent")
{
           user = new Agent();
基本上我认为你误解了多态性。您可以将实例向上投射到其父实例之一,即

User user = new Agent();

....Later....

Agent agent = user as Agent;

....or.....
Agent agentTwo = new Agent;
User agentAsUser = agentTwo as User;

但你不能投另一种方式。如果你仔细想想,这是很有道理的——当应用程序创建内存来保存数据时,它只知道你用new告诉它什么。

你不能将你实例化为超类的对象强制转换为子类,因为它不是那种类型,即User类型的对象永远不能是Agent类型


您需要重新构造代码,以便根据从数据库检索到的类型将对象实例化为正确的具体类。

您已将用户声明为
用户,而不是
代理或
客户端。因此,您不能将该对象强制转换为
代理
客户端
,因为它不是
代理
客户端
,而是
用户

您必须更改代码,使其看起来像这样。(窃笑):


user
变量的实例属于
user
类型,在这种情况下,不能将
base
类强制转换为派生类

我建议将
用户
作为
抽象类
,提供新的方法

abstract User BuildFromDataReader(IDataReader) 

因此,
Client
Agent
都将提供如何从
DataReader

构建的自己的实现多态性意味着您可以将代理实例视为用户,而不是将用户实例视为代理

User returnuser;
string sql = "SELECT usertype, datecreated, email, name, phone, country, ip, company, companyreg, securityquestion, securityanswer, description, accountbalance, aothcredits, wantsrequests FROM ruser WHERE userid=@userid";        
try 
   {
        using (SqlConnection con = new SqlConnection(constr))
        using (SqlCommand cmd = new SqlCommand(sql))
        {
            con.Open();
            cmd.Connection = con;
            cmd.Parameters.Add("@userid", System.Data.SqlDbType.Int).Value = userid;
            using (SqlDataReader rdr = cmd.ExecuteReader())
            {
                if (rdr.Read())
                {
                    string type = rdr.GetString(0);
                    if (type == "agent")
                    {
                        Agent agent = user as Agent;
                        agent.Company = rdr["company"].ToString();
                        agent.CompanyReg = rdr["companyreg"].ToString();
                        agent.SecurityQuestion = rdr["securityQuestion"].ToString();
                        agent.SecurityAnswer = rdr["securityanswer"].ToString();
                        agent.Description = rdr["description"].ToString();
                        agent.AccountBalance = (int)rdr["accountbalance"];
                        agent.WantsRequests = Convert.ToBoolean(rdr["wantsrequests"]);
                        returnuser = agent;
                    }
                    else //type == "client"
                    {
                        Client client = user as Client;
                        client.Country = rdr["country"].ToString();
                        client.IP = rdr["ip"].ToString();
                        returnuser = client;
                    }
                    returnuser.UserId = userid;
                    returnuser.UserType = rdr["usertype"].ToString();
                    returnuser.DateCreated = (DateTime)rdr["datecreated"];
                    returnuser.Email = rdr["email"].ToString();
                    returnuser.Name = rdr["name"].ToString();
                    returnuser.Phone = rdr["phone"].ToString();

                }
            }
        }
    }
    catch (SqlException e)
    {
        throw e;
    }
    return returnuser;
}

您可以将returnUser定义为User,但必须使用正确的类型创建它,例如:

public User GetUser(int userid)
{
    User returnuser;
    string sql = "SELECT usertype, datecreated, email, name, phone, country, ip, company, companyreg, securityquestion, securityanswer, description, accountbalance, aothcredits, wantsrequests FROM ruser WHERE userid=@userid";
    try
    {
        using (SqlConnection con = new SqlConnection(constr))
        using (SqlCommand cmd = new SqlCommand(sql))
        {
            con.Open();
            cmd.Connection = con;
            cmd.Parameters.Add("@userid", System.Data.SqlDbType.Int).Value = userid;
            using (SqlDataReader rdr = cmd.ExecuteReader())
            {
                if (rdr.Read())
                {
                    string type = rdr.GetString(0);

                    if (type == "agent")
                    {
                        Agent agent = new Agent();
                        agent.Company = rdr["company"].ToString();
                        agent.CompanyReg = rdr["companyreg"].ToString();
                        agent.SecurityQuestion = rdr["securityQuestion"].ToString();
                        agent.SecurityAnswer = rdr["securityanswer"].ToString();
                        agent.Description = rdr["description"].ToString();
                        agent.AccountBalance = (int)rdr["accountbalance"];
                        agent.WantsRequests = Convert.ToBoolean(rdr["wantsrequests"]);
                        returnuser = agent;
                    }
                    else //type == "client"
                    {
                        Client client = new Client();
                        client.Country = rdr["country"].ToString();
                        client.IP = rdr["ip"].ToString();
                        returnuser = client;
                    }
                    returnuser .UserId = userid;
                    returnuser .UserType = rdr["usertype"].ToString();
                    returnuser .DateCreated = (DateTime)rdr["datecreated"];
                    returnuser .Email = rdr["email"].ToString();
                    returnuser .Name = rdr["name"].ToString();
                    returnuser .Phone = rdr["phone"].ToString();

                }
            }
        }
    }
    catch (SqlException e)
    {
        throw e;
    }
    return returnuser;
}

一种可能的方法是,在代理类和客户端类上有一个构造函数,它接受一个用户参数(基本上使它们成为用户类的装饰器),从而实现您想要做的事情

所以,

public class Agent : User
{
   public Agent(User user)
   {
   }
}
因此,在GetUser(intuserid)方法中,您现在可以执行以下操作

if (type == "agent")                        
{                            
    Agent agent = new Agent(user);
    agent.Company = rdr["company"].ToString();                           
    ..
    ..
    returnuser = agent;                        
 }

希望这有助于解决问题。

要补充其他答案,请想象它在编写时会起作用。考虑这种情况:

var ape = new Ape();
var animal = ape as Animal;      // Animal is base class of Ape and Giraffe
var giraffe = animal as Giraffe;
如果最后一行的结果确实是一个非空的长颈鹿对象,那么你将神奇地把一只猿变成长颈鹿


因此,基本上:你可以将一个孩子强制转换为一个家长,但你只能将所讨论的对象从家长强制转换为一个孩子,如果该对象实际上是该儿童类型或其后代。

每辆自行车都是一辆车,但每辆车都是一辆自行车吗?

什么问题?恐怕你没提那件事EntityFramework(如果您想使用ORM工具)或LinqToSql(用于较低的数据库访问)等框架很好地完成了您在这里所做的工作。有什么理由不使用这个吗?也许Jesper应该重新格式化他的问题。问题就在他的代码示例上说明了,但有点看不见;)顺便说一句:如果它是唯一的构造函数,则不需要提供默认构造函数(不带参数的构造函数)。@J.Steen,除非对象实际上是子类的类型,否则他不能提供默认构造函数。在所有情况下,我建议Jesper将用户类标记为抽象:
公共抽象类user
。这将有助于正确设计应用程序。这一个(由xan提供)运行良好。return语句需要在if(rdr.read())-部分中,否则用户永远不会被实例化。我想感谢你们提供的信息和答案…@Steve B,是的,我删除了我的评论。不知什么原因,我是以另一种方式阅读的同意
抽象类
,但是我不建议将此方法添加到
客户端
代理
,因为它将违反关注点分离。如果这样做:public User GetUser(int userid){User User User User;…If(rdr.Read()){string type=rdr.GetString(0);if(type==“agent”){user=new agent();user.Company=rdr[“Company”].ToString();它不起作用。公司下面有一条红线,上面写着:用户不包含公司的定义。为了访问特定属性,您当然需要强制转换。我想您需要一些OO方面的阅读/课程。(检查我编辑的代码)
public class Agent : User
{
   public Agent(User user)
   {
   }
}
if (type == "agent")                        
{                            
    Agent agent = new Agent(user);
    agent.Company = rdr["company"].ToString();                           
    ..
    ..
    returnuser = agent;                        
 }
var ape = new Ape();
var animal = ape as Animal;      // Animal is base class of Ape and Giraffe
var giraffe = animal as Giraffe;