views:

105

answers:

5

Hi, I am new to C#. I have a Persons class and a User class which inherits from the Persons class. I my console I input a users in an array. Then I can add a note to a user that is in the users array by only entering the users id. In my Persons class i have this function that has to search if this user is in the users array.

public static Persons FindPerson(Persons[] persons, int noteid)
{
    foreach (Persons person in persons)
        if (person.ID == noteid) return person;
    return null;
}

In my User class I have a function that loops the whole input of the id until it gets an id that is in the users array.

public static User SelectUser(User[] users)
{
    while (true)
    {
        Console.Write("Please enter the User id: ");
        string input = Console.ReadLine();
        int id;
        if (int.TryParse(input, out id))
        {
            Persons person = Persons.FindPerson(users, id);
            if (person != null) return person; // fail on "return person"                   
        }
        Console.WriteLine("The User does not exist. Please try again.");                
    }
}

Everything works fine except that I now get this error message on the "return person" in the if statement.

Cannot implicitly convert type 'UserCustomerNotes.Persons' to 'UserCustomerNotes.User'. An explicit conversion exists (are you missing a cast?)

Can anyone please help? Thanks in advance.

+3  A: 

Since User inherits from Person, you cannot implicitly convert any random Person to a User (though you can implicitly convert a User to a Person).

Since you pass a User[] to FindPerson(users, id), you can be sure the returned person is indeed a User, so you can cast it like this:

return (User)person;

Edit: You might consider using generics on FindPerson to avoid the cast altogether.

public static T FindPerson<T>(IEnumerable<T> persons, int noteid)
    where T : Person
{
    foreach (T person in persons)
    {
        if (person.ID == noteid)
        {
            return person;
        }
    }

    return null;
}

public static User SelectUser(User[] users)
{
    while (true)
    {
        Console.Write("Please enter the User id: ");
        string input = Console.ReadLine();
        int id;
        if (int.TryParse(input, out id))
        {
            User person = Persons.FindPerson(users, id);
            if (person != null)
            {
                return person;
            }            
        }

        Console.WriteLine("The User does not exist. Please try again.");                
    }
}
Zach Johnson
+4  A: 

Because a Person is not nessecarily a User, the compiler is not able to implicitly convert a Person to a User. In your particular case, since you know you have a list of Users, you can explicitly tell it, "I know this Person is actually a User" with the following:

if (person != null)
   return (User) person;

The cast ((User)) will throw an exception at runtime if the instance is not actually a User, but since you've started with a collection of Users to begin with, you don't need to worry.

Dean Harding
Many thanks! This worked! I now feel so stupid, cant belive that I have overlooked this solution. Thanks again! :-)
CSharpSuzie
+2  A: 

You should rewrite the return snippet like so:

User user = Persons.FindPerson(users, id) as User;
if (user != null) return user;

The problem you were having was to do with you trying to return a base class from a method that should return a more derived class. The compiler cannot automatically downcast(Persons->User), but it can upcast (User->Persons)

Igor Zevaka
I don't like this. If you have a list of objects and the assumption is that they are all "Users" under the hood, it should blow up at runtime if that is not the case. This just (potentially) silently hides a bug. Sometimes crashing is good.
Ed Swangren
@Ed: I agree that this can be an error waiting to happen if the input values aren't controlled. However the "as" keyword is one of the most (if not the most) efficient way to handle a polymorphic type. I would suggest a generic template that enforced the input type so that the "as" keyword could safely be used. Some pros and cons here: http://theburningmonk.com/2010/04/net-tips-using-as-vs-casting-in-csharp/
Tim
@Ed In this case you are correct, in fact `SelectUser` function signature guarantees that an object returned will be of type `User` since we pass in `User[]`. More generally `as` versus cast is a robustness versus correctness argument where you have to make a call as to which way you are leaning. I would definitely use `as` if `Persons.FindPerson` does not take the data array and retrieves the person data by itself.
Igor Zevaka
+1  A: 

You need to implement explicit or implicit operator:

class ClassA
{
    public string Property1 { get; set; }

    public static explicit operator ClassB(ClassA classA)
    {
        return new ClassB() { Property2 = classA.Property1 };
    }
}

class ClassB
{
    public string Property2 { get; set; }
}

var a = new ClassA() {Property1 = "test"};
ClassB b = (ClassB)a;
Console.WriteLine(b.Property2); // output is "test"
Pranay Rana
A: 

Anyway, I recommend to use LINQ extension method for your first method:

using System.Collections.Generic;
using System.Linq;

public static Persons FindPerson(IEnumerable<Person> persons, int id)
{
    return persons.FirstOrDefault(p => p.ID == id);
}

Looks much better, right?

abatishchev