Полиморфные проблемы C # - PullRequest
3 голосов
/ 27 июля 2011

У меня есть таблица БД с пользователями, некоторые являются «агентами», а некоторые - «клиентами».В моем проекте на C # у меня есть суперкласс User и подкласс агента и клиента.Агент и Клиент расширяют пользователя.

У меня возникают некоторые основные проблемы при приведении или изменении объекта пользователя на объект агента или клиента.Я действительно не знаю почему.Это, вероятно, довольно просто, но я не знаю, что не так.

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;
    }

Ответы [ 10 ]

7 голосов
/ 27 июля 2011

Поскольку вы создали его со строкой

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;

Но вы не можете использовать другой способ.Разумеется, если вы подумаете об этом, - когда приложение создает память для хранения данных, оно знает только то, что вы сказали ей с новым.

6 голосов
/ 27 июля 2011

Невозможно привести из базового класса в дочерний класс, если создать экземпляр объекта в качестве базового класса.

Вы пытаетесь использовать as для приведения из User в Client или Agent в зависимости от ваших данных. Однако вы явно создаете объект User в начале вашей функции:

User user = new User();

Этот объект имеет тип User, поэтому as не сможет преобразовать его в Client или Agent и вернет ноль. Смотри документацию здесь .

Оператор 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

Что вам нужно сделать, это явно создать наиболее конкретный класс, который вам нужен, либо новый 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;
        }
    }
5 голосов
/ 27 июля 2011

Вы не можете привести объект, который вы создали как суперкласс, к подклассу, так как он не относится к этому типу, то есть объект типа User никогда не может быть типом агента.

You 'Вам потребуется реструктурировать ваш код, чтобы создать экземпляр объекта как правильный конкретный класс в зависимости от типа, который вы извлекаете из базы данных.

2 голосов
/ 27 июля 2011

Вы объявили своего пользователя как 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.
    }
}
1 голос
/ 27 июля 2011

Вы можете определить 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 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;
}
1 голос
/ 27 июля 2011

Полиморфизм означает, что вы можете рассматривать экземпляр Агента как пользователя, а не экземпляр пользователя как агента.

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;
}
1 голос
/ 27 июля 2011

Экземпляр переменной user имеет тип User, вы не можете привести класс base к производному в таком случае

Я бы предложил сделать User как abstract class, предоставьте новый метод

abstract User BuildFromDataReader(IDataReader) 

, так что Client и Agent обеспечат собственную реализацию инструкции по сборке из DataReader

0 голосов
/ 27 июля 2011

Каждый велосипед - это транспортное средство, но является ли каждое транспортное средство велосипедом?

0 голосов
/ 27 июля 2011

Чтобы дополнить другие ответы: представьте, что это сработает так, как вы написали. Рассмотрим этот сценарий:

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

Если последняя строка действительно приведет к ненулевому объекту Жираф, то вы волшебным образом превратите обезьяну в Жирафа.

Так что в основном: вы всегда можете преобразовать дочерний элемент в родителя, но вы можете преобразовать дочерний элемент в родительский элемент только в том случае, если рассматриваемый объект действительно принадлежит к этому дочернему типу или его потомку.

0 голосов
/ 27 июля 2011

Один из возможных способов добиться того, что вы пытаетесь сделать, - это иметь конструктор классов 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;                        
 }

Надеюсь, это поможет делу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...