2011-07-27 33 views
3

我有一個數據庫表與用戶,一些是'代理',一些是'客戶'。在我的C#項目中,我有一個User超類和一個Agent和Client子類。代理和客戶擴展用戶。多態性問題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 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 [email protected]"; 
     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; 
    } 
+3

有什麼問題?恐怕你沒有提到這一點。 =) –

+1

您在這裏所做的工作可以通過EntityFramework之類的框架(如果您想使用ORM工具)或LinqToSql(用於較低的DB訪問)完成。有沒有理由不使用它? –

+0

也許Jesper應該重新格式化他的問題。這個問題在他的代碼示例上有說明,但有點不可見;)Btw:如果它是唯一的,你不需要提供默認構造函數(不帶參數的構造函數)。 – Zebi

回答

6

您不能從基類轉換爲子類,如果你將該對象實例化爲基類。

您正試圖使用​​asUser轉換爲ClientAgent取決於您的數據。但是,您explicately在你的函數開始創建User對象:

User user = new User(); 

這個對象是User類型,以便將as無法將其轉換爲一個ClientAgent,將返回null。請參閱文檔here

as運算符就像一個強制類型轉換,除非它在轉換失敗而不是引發異常時產生空值。

可以證明這一點,如下所示:

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 

你需要做的是明確創建你所需要的最具體的類,無論是新AgentClient,然後抹上這對一個更通用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; 
     } 
    } 
+1

@J。 Steen,除非對象實際上是子類的類型,否則他不行。在所有情況下,我建議Jesper標記用戶類摘要:'public abstract class User'。這將有助於正確設計應用程序。 –

+0

這個(xan)效果很好。 return語句需要放在if(rdr.read())部分中,否則用戶永遠不會實例化。我想感謝大家的信息和答案... – Jesper

+0

@Steve B,是的,我刪除了我的評論。我以其他方式閱讀它,原因不明。 =) –

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; 

但您無法以其他方式進行轉換。如果你考慮這個問題,它應該是合理的 - 當應用程序創建內存來保存數據時,它只知道你用新的方式告訴它的內容。

5

,因爲它是該類型的不是你不能投你有實例作爲超對象的子類,即User類型的對象可從來就不是一個類型的代理。

您需要重構代碼,以便根據從數據庫中檢索的類型將對象實例化爲正確的具體類。

2

您已將您的用戶聲明爲User,而不是AgentClient。因此,您不能將該對象投射到AgentClient,因爲它不是AgentClient,它是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. 
    } 
} 
+0

如果我這樣做: public User GetUser(int userid) { User user; ... if(rdr.Read()) { string type = rdr.GetString(0); if(type ==「agent」) { user = new Agent(); user.Company = rdr [「company」]。ToString(); 它不起作用。公司下方有一條紅線,它表示: 用戶不包含公司的定義 – Jesper

+0

爲了訪問特定的屬性,您必須投放課程。我認爲你需要一些關於面向對象的閱讀/課程。 (檢查我編輯的代碼) –

1

一個user變量的實例是User類型的,你可以不投一個base類的在這種情況下派生

我會建議做Userabstract class,提供新的方法

abstract User BuildFromDataReader(IDataReader) 

這樣既ClientAgent將提供自己的實現如何,從建設210

+1

同意'抽象類',但我不會建議將此方法添加到'Client'和'Agent',因爲它會違反關注點分離。 – Zebi

1

多態性意味着您可以將代理的實例視爲用戶,而不是用戶的實例作爲代理。

User returnuser; 
string sql = "SELECT usertype, datecreated, email, name, phone, country, ip, company, companyreg, securityquestion, securityanswer, description, accountbalance, aothcredits, wantsrequests FROM ruser WHERE [email protected]";   
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; 
} 
1

您可以定義returnUser作爲用戶,但是必須使用正確的類型,即是這樣創造的:

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 [email protected]"; 
    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; 
} 
0

一種可能的方式,你可以達到你正在嘗試做的是上具有代理和客戶端類構造這需要在用戶參數(基本上使它們的用戶類的裝飾)

因此,

public class Agent : User 
{ 
    public Agent(User user) 
    { 
    } 
} 

因此,在您的getUser(INT用戶id)方法,你現在可以做這樣的事情

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

希望有所幫助的原因。

0

增加其他答案:想象一下,它會像你寫的那樣工作。考慮這種情況:

var ape = new Ape(); 
var animal = ape as Animal;  // Animal is base class of Ape and Giraffe 
var giraffe = animal as Giraffe; 

如果最後一行的確會導致一個非空長頸鹿對象,然後你會奇蹟般地猿變成長頸鹿。

所以基本上:你總是可以投一個孩子變成父母,但你只能從父轉換爲子當且僅當有問題的對象實際上是子類型或其後代的是。

0

每輛自行車都是一輛車,但是每輛車都是一輛自行車?