tags:

views:

201

answers:

8

The following code gives me the errors:

Cannot implicitly convert type T to string.
Cannot implicitly convert type T to int.

What do I have to do to get this method to return the type of variable I define with T when I call it?

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

namespace TestGener234
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("his first name is {0}", GetPropertyValue<string>("firstName"));
            Console.WriteLine("his age is {0}", GetPropertyValue<int>("age"));
        }

        public static T GetPropertyValue<T>(string propertyIdCode)
        {
            if (propertyIdCode == "firstName")
                return "Jim";
            if (propertyIdCode == "age")
                return 32;
            return null;
        }
    }
}

Addendum:

Here is a more complete example of why I needed the generic solution, i.e. I have a class that saves its values as strings no matter what the type, and this generic solution simply makes the calling code cleaner:

using System;
using System.Collections.Generic;

namespace TestGener234
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Item> items = Item.GetItems();
            foreach (var item in items)
            {
                string firstName = item.GetPropertyValue<string>("firstName");
                int age = item.GetPropertyValue<int>("age");
                Console.WriteLine("First name is {0} and age is {1}.", firstName, age);
            }
            Console.ReadLine();
        }
    }

    public class Item
    {
        public string FirstName { get; set; }
        public string Age { get; set; }


        public static List<Item> GetItems()
        {
            List<Item> items = new List<Item>();
            items.Add(new Item { FirstName = "Jim", Age = "34" });
            items.Add(new Item { FirstName = "Angie", Age = "32" });
            return items;
        }

        public T GetPropertyValue<T>(string propertyIdCode)
        {
            if (propertyIdCode == "firstName")
                return (T)(object)FirstName;
            if (propertyIdCode == "age")
                return (T)(object)(Convert.ToInt32(Age));
            return default(T);
        }
    }
}
+1  A: 

GetPropertyValue<string>("age") wants to return a string. Change it to GetPropertyValue<int>("age") and it will work as long as "age" is your parameter value.

Your implementation would be better off getting the type of the generic parameter T in order to choose what to return instead of basing it on the function parameter.

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

namespace TestGener234
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("his first name is {0}", GetPropertyValue<string>("firstName"));
            Console.WriteLine("his age is {0}", GetPropertyValue<int>("age"));
        }

        public static T GetPropertyValue<T>(string propertyIdCode)
        {
            if (typeof(T) == typeof(string) && propertyIdCode == "firstName")
                return "Jim";
            if (typeof(T) == typeof(string) && propertyIdCode == "age")
                return "32";
            if (typeof(T) == typeof(int) && propertyIdCode == "age")
                return 32;
            throw (new ArgumentException());
        }
    }
}
NickLarsen
That may be true, but it is unrelated to the compile error, which is *inside* `GetPropertyValue`
Marc Gravell
sorry, that was just a typo, corrected
Edward Tanguay
+8  A: 

That is troublesome; to make the compiler happy you can double-cast, but that implies a box for value types:

    public static T GetPropertyValue<T>(string propertyIdCode)
    {
        if (propertyIdCode == "firstName")
            return (T)(object)"Jim";
        if (propertyIdCode == "age")
            return (T)(object)32;
        return default(T);
    }

In reality, I think you may be better just using an object return type.

Marc Gravell
That is exactly what I wanted, thanks, but I would be interested to know though why you think returning object would be better, I thought that was the problem that generics solves: allowing you to return type-safe variables.
Edward Tanguay
@Edward: Deciding return type based on input parameters is confusing and can lead to errors. What if there is mismatch between type provided in the method call and the actual type of the object being returned. Boom. You have an exception.You can use (int)GetPropertyValue("age") (if you decided to do this without generics as indicated in my answer) but even this can cause an exception.I would split this method into multiple methods each of those methods would then return one information item (age, name, etc..).
chikak
Yes, but - you're using it in a strange way. Your method decides depending on the _content_ of the parameter what type it returns. Your way to use generics is just a fancy way to cast, really. if you use GetPropertyValue<string>( or (string)GetPropertyValue( doesn't change the fact that you, as the caller, _have_ to know the return type and specify it explicitly. You win nothing, imo.
Benjamin Podszun
@Benjamin: I like this "Your way to use generics is just a fancy way to cast" :-)
chikak
it just means I don't have to do the messy caching in the calling code, it is blackboxed in the method, what I win is simplified calling code
Edward Tanguay
By using double casting, you are playing tricks with the compiler. This code is not typesafe if that is the reason you want to use generics.
chikak
A: 
    public static T GetPropertyValue<T>(string propertyIdCode)
    {
        object result = null;
        if (propertyIdCode == "firstName")
            result = "Jim";
        if (propertyIdCode == "age")
            result = 32;
        return result == null ? default(T) : (T)result;
    }
Jamie Ide
+1  A: 

You can return object from GetPropertyValue and then do a cast. You are trying to use a generic method to return specific types depending on input parameters. Sounds confusing :-)

public static object GetPropertyValue(string propertyIdCode)
    {
        if (propertyIdCode == "firstName")
            return "Jim";
        if (propertyIdCode == "age")
            return 32;
        return null;
    }

and then cast (int)GetPropertyValue("age");

chikak
hmm, why do you think int age = GetPropertyValue<int>("age") is more confusing than (int)GetPropertyValue("age")? it just seems more type safe to me (???)
Edward Tanguay
What is confusing is that "age" is deciding the flow in your method which is then returning an int. Input parameter is affecting the type of output parameter. if I changed the method code to if (propertyIdCode == "age") return new ComplexObject(); this will lead to runtime errors and you are no longer protected by type safety offered by generics
chikak
+2  A: 

This should work...

  public static T GetPropertyValue<T>(string propertyIdCode)
  {
     if (propertyIdCode == "firstName")
        return (T)Convert.ChangeType("Jim", typeof(T));
     if (propertyIdCode == "age")
        return (T)Convert.ChangeType(22, typeof(T));
     return default(T);
  }
Binoj Antony
+1  A: 

Usually when you are casting inside a generic method, it is a design problem. Usually, you want to keep your type generic inside your method (no casting, no braching based on type), something like this:

public class Properties<T>
{
    private Dictionary<string, T> _dict = new Dictionary<string, T>();

    public void SetPropertyValue<T>(string propertyIdCode, T value)
    {
        _dict[propertyIdCode] = value;
    }

    public T GetPropertyValue<T>(string propertyIdCode)
    {
        return _dict[propertyIdCode];
    }
}

On, the other hand, if you want to access object's properties through their name (it seems like this is what you are doing, sorry if I got it wrong), the right way would be to use reflection (PropertyInfo.GetValue):

public object GetPropertyValue(object obj, string propertyIdCode)
{
    PropertyInfo pinfo = obj.GetType().GetProperty(propertyIdCode);
    return pinfo.GetValue(obj, null);
}
Groo
This assumes a single type of value for the property!
Stevo3000
Yes, exactly like `List<T>`, `Dictionary<Tk,Tv>` and other generic classes. This is an example of a reasonable use of generics, not the way to solve the OP's problem.
Groo
A: 

Marc's example of double-casting is the correct way to get the compiler to behave correctly.

You could write a sperate method for each value type and have a generic method for reference types. This would stop stop boxing on value types.

This is only useful if the objects being accessed are not boxed for storage (e.g. not stored as an object).

public static T GetPropertyValue<T>(string propertyIdCode)
{
}

public static int GetPropertyInt(string propertyIdCode)
{
}
Stevo3000
+3  A: 

This is an abuse of generics. If you have a small number of types that the generic type parameter could possibly be then just replace it with that many methods:

string GetTextProperty(string propertyName) { ... }
int GetNumberProperty(string propertyName) { ... }
Giraffe GetGiraffeProperty(string propertyName) { ... }
Eric Lippert
Maybe its just a sample he provided and not the actual...
John G
yes I could do that but it is just more code, this (mis)use of generics works just as well and is less code, call me a pragmatist
Edward Tanguay