我有一个用户的数据库表,有些是'代理',有些是'客户'。在我的C#项目中,我有一个User超类和一个Agent和Client子类。代理和客户扩展用户。
在将User对象转换或更改为Agent或Client对象时,我遇到了一些基本问题。我真的不知道为什么。这可能是相当基本的,但我不知道出了什么问题。
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 class Agent : User
{
public string Company { get; set; }
public string CompanyReg { get; set; }
public string SecurityQuestion { get; set; }
public string SecurityAnswer { get; set; }
public string Description { get; set; }
public int AccountBalance { get; set; }
public bool WantsRequests { get; set; }
public string ImageUrl { get; set; }
public Agent()
{
}
}
public class Client : User
{
public string Country { get; set; }
public string IP { get; set; }
public Client()
{
}
}
现在为什么我不能这样做:
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;
}
答案 0 :(得分:7)
因为您使用
行创建了它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;
但你不能反过来。如果你考虑它,这是有道理的 - 当应用程序创建内存来保存数据时,它只知道你用新的东西告诉它。
答案 1 :(得分:6)
如果将对象实例化为基类,则无法从基类强制转换为子类。
您正尝试使用as
从User
投射到Client
或Agent
,具体取决于您的数据。但是,您在函数的开头部分明确地创建了一个User
对象:
User user = new User();
此对象的类型为User
,因此as
将无法将其转换为Client
或Agent
并返回null。请参阅文档here。
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
您需要做的是明确创建所需的最具体的课程,可以是新的Agent
或Client
,然后将其转换为更通用的User
。需要。
例如:
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;
}
}
答案 2 :(得分:5)
您不能将已实例化为超类的对象转换为子类,因为它不属于该类型,即User类型的对象永远不会是一种代理。
您需要重新构建代码,以便根据从数据库中检索的类型将对象实例化为正确的具体类。
答案 3 :(得分:2)
您已将自己的用户声明为User
,而不是Agent
或Client
。因此,您无法将该对象投射到Agent
或Client
,因为它不是Agent
或Client
,而是User
。
您必须更改代码,使其看起来像这样。 (SNIPPIT):
using (SqlDataReader rdr = cmd.ExecuteReader())
{
if(rdr.Read())
{
User user;
string type = rdr.GetString(0);
if (type == "agent")
{
user = new Agent();
// Fill out Agent specific properties
var agent = user as Agent;
agent.Company = ...
}
else if( type == "client" )
{
user = new Client();
// Fill out Client specific properties
var client = user as Client;
}
else
{
throw new InvalidProgramException ("Unknown user-type");
}
// Fill out common User properties.
}
}
答案 4 :(得分:1)
user
变量的实例属于User
类型,在这种情况下,您无法将base
类强制转换为派生
我建议将User
设为abstract class
,提供新方法
abstract User BuildFromDataReader(IDataReader)
因此,Client
和Agent
都将提供自己的实施方式DataReader
答案 5 :(得分:1)
多态性意味着您可以将代理实例视为用户,而不是将用户实例视为代理。
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;
}
答案 6 :(得分:1)
您可以将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;
}
答案 7 :(得分:0)
一种可能的方法,你可以实现你想要做的就是在Agent和Client类上有一个构造函数,它接受一个User参数(基本上使它们成为User类的装饰器)
因此,
public class Agent : User
{
public Agent(User user)
{
}
}
因此,在您的GetUser(int userid)方法中,您现在可以执行类似
的操作if (type == "agent")
{
Agent agent = new Agent(user);
agent.Company = rdr["company"].ToString();
..
..
returnuser = agent;
}
希望有助于事业。
答案 8 :(得分:0)
增加其他答案:想象一下,它会在你写的时候起作用。请考虑以下情况:
var ape = new Ape();
var animal = ape as Animal; // Animal is base class of Ape and Giraffe
var giraffe = animal as Giraffe;
如果最后一行确实会导致非空的长颈鹿对象,那么你就会把Ape神奇地变成长颈鹿。
所以基本上:你总是可以将一个孩子投射到父母身上,但是如果有问题的对象实际上属于那个孩子类型或其后代,你只能从父母施放到孩子。
答案 9 :(得分:0)
每辆自行车都是车辆,但每辆车都是自行车吗?