Accessing property of a generic argument

Viewed 58

I want to create a method with generic argument person. person can be either Man or Woman but they are just a class not inheriting from any base. VS tells me that there is no definition for Age in T.

public class Man
{
    public string Name { get; set; } = "Man Name";
    public int? Age { get; set; }
}

public class Woman
{
    public string Name { get; set; } = "Woman Name";
    public int? Age { get; set; }
}

public class People
{
    public string Person<T>(T person)
    {
        return person.Age;
    };
}

I've search and found some solutions Accessing properties of a generic types and it seems like I have to create 2 overloading methods. As they both do the same thing, is there anyway to tell VS that T has a property Age ?

Edit: I cannot change the classes Man & Woman. I'm writing the Person class.

Thanks.

Edit: Based on @Panagiotis Kanavos suggestions in the answer below, I tried the following and it worked. I am now able to assign the 2 different classes' properties to the same variable (_age) and continue processing using 1 class and 1 set of logic without using generic type. I will try this in my actual program. Thanks very much.

public class People
{
    public void Person(object person)
    {
        int? _age = 0;
        switch (person.GetType().ToString())
        {
            case "Man":
                var man = person as Man;
                _age = man?.Age;
                break;
            case "Woman":
                var woman = person as Woman;
                _age = woman?.Age;
                break;
        }
        Console.WriteLine(_age);
    }
}
2 Answers

You need to add an interface, or a base class, to your classes and restrict your generic type to this type:

public interface IPerson
{
    string Name { get;  } 
    int? Age { get; }
}
public class Man : IPerson{...}
public class Woman : IPerson{...}
public string Person<T>(T person) where T : IPerson
{
        return person.Age;
};

This will of course make your Person class & method completely unnecessary if you are not doing anything more complex, since you can just do:

IPerson p = new Man();
var age = p.Age;

or write a method that just takes a IPerson parameter directly.

You can also let the caller define how get some parameter by injecting a delegate, i.e. Func<T, int> getAge. But again, for such a trivial example you are just making things more complicated for no actual gain.

It would be nice to know what the actual problem is, and what the actual types look like. Generics can't be used if the classes have no common type. The compiler needs to be able to check if person.Age is valid at compile time. If you pass the wrong type you get a compilation error. This is done by using type constraints that tell the compiler that T implements a specific interface or inherits from a specific type.

If the classes can't be modified, you'd have to use reflection, dynamics or pattern matching to check for the existence of the method and call it. You'd also have to handle error cases.

Pattern Matching

Using patter matching, you can write :

public class People
{
    public string Person(object person)
    {
        return switch (person) 
        {
            Man m=>m.Age?.ToString(),
            Woman w=>w.Age?.ToString(),
            _ => ""
        };
    };
}

This method returns an empty string if an unknown type is used. The code could be modified to throw an exception, eg :

public string Person(object person)
{
    return switch (person) 
    {
        Man m=>m.Age?.ToString(),
        Woman w=>w.Age?.ToString(),
        _ => throw new ArgumentException("Unexpected type",nameof(person));
    };
};

There's no reason to make this method generic. Without type constraints, the only methods that can be called are those provided by System.Object.

Extension Methods

You can create an extension method for each concrete type that can be called as if it was an actual instance method. The advantage is that the compiler itself will complain if the wrong type is used :

public static class PersonExtensions
{
    public static string Person(dynamicMan person)
    {
        return person.Age?.ToString();
    }

    public static string Person(this Woman person)
    {
        return person.Age?.ToString()
    };
}

Dynamic typing

Using dynamic would allow the code to compile but would result in runtime exceptions if the method is missing. Dynamic typing is more expensive too.

public string Person(dynamic person)
{       
    return m.Age?.ToString();
}
Related