2

I have a class:

class abc <T> {
  private T foo; 
  public string a {
    set {
       foo = T.parse(value);
    }

    get{
       return foo.toString();
    }

  }
}

However the T.parse command is giving me an error. Anyone of a way to do what I am trying to do?

I am using this as a base class for some other derived classes.

Edit:

What I ended us doing:

Delegate parse = Delegate.CreateDelegate(typeof(Func<String, T>), typeof(T).GetMethod("Parse", new[] { typeof(string) }));

I do that once in the constructor

and then I do the following in my property:

        lock (lockVariable)
        {
            m_result = (T)parse.DynamicInvoke(value);
            dirty = true;
        } 
SamFisher83
  • 3,937
  • 9
  • 39
  • 52

8 Answers8

8

C# generic types are not C++ templates. A template lets you do a fancy "search and replace" where you would substitute the name of a type that implements a static parse method for T. C# generics are not a textual search-and-replace mechanism like that. Rather, they describe parameterized polymorphism on types. With a template, all that is required is that the specific arguments you substitute for the parameters are all good. With a generic every possible substitution whether you actually do it or not, has got to be good.

UPDATE:

A commenter asks:

What would be the C# way of doing things when an equivalent to Haskell's Read type class is needed?

Now we come to the deep question underlying the original question.

To clarify for the reader unfamiliar with Haskell: Since C# 2.0, C# has supported "generic" types, which are a "higher" kind of type than regular types. You can say List<int> and a new type is made for you that follows the List<T> pattern, but it is a list specifically of integers.

Haskell supports an even higher kind of type in its type system. With generic types you can say "every MyCollection<T> has a method GetValue that takes an int and returns a T, for any T you care to name". With generic types you can put constraints on T and say "and furthermore, T is guaranteed to implement IComparable<T>..." With Haskell typeclasses you can go even further and say the moral equivalent of "...and moreover, T is guaranteed to have a static method Parse that takes a string and returns a T".

The "Read" typeclass is specifically that typeclass that declares the moral equivalent of "a class C that obeys the Read typeclass pattern is one that has a method Parse that takes a string and returns a C".

C# does not support that kind of higher type. If it did then we could typecheck patterns in the language itself such as monads, which today can only be typechecked by baking them into the compiler (in the form of query comprehensions, for example.) (See Why there is no something like IMonad<T> in upcoming .NET 4.0 for some more thoughts.)

Since there is no way to represent that idea in the type system, you're pretty much stuck with not using generics to solve this problem. The type system simply doesn't support that level of genericity.

People sometimes do horrid things like:

static T Read<T>(string s)
{
    if (typeof(T) == typeof(int)) return (T)(object)int.Parse(s);
    if ...

but that is in my opinion a bit abusive; it really is not generic.

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • What would be the C# way of doing things when an equivalent to Haskell's Read type class is needed? – dtb Oct 14 '11 at 22:33
  • Thanks for the update; it explains well why a solution checked at compile-time is not possible in C#. What approach would you recommend for a solution checked at run-time; e.g. reflection, a static Dictionary, ...? – dtb Oct 14 '11 at 23:35
3

You could use reflection. You cannot access static members through a generic parameter.

class Abc<T> {
    private T foo; 
    public string a {
        set {
            foo = Parse<T>(value);
        }

        get {
            return foo.ToString();
        }
    }

    static T Parse<T>(string s)
    {
        var type = typeof(T);
        var method = type.GetMethod("Parse", new[] { typeof(string) });
        return (T)method.Invoke(null, new[] { s });
    }
}
Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
2

C# doesn't have templates. .NET generics don't work like C++ templates.

With an appropriate constraint, you can use instance methods on parameters with generic type, but there's no way to constrain static members.

However, you could use reflection, something along the lines of typeof(T).GetMethod("Parse"), to make a Func<string,T> delegate.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
1

You can't call a static method on a generic class. Look at this post: Calling a static method on a generic type parameter

But here is a little workaround:

public interface iExample
{
    iExample Parse(string value);
}

class abc<T> where T : iExample, new()
{
    private T foo;
    public string a
    {
        set
        {
            foo = (T)(new T().Parse(value));
        }

        get
        {
            return foo.ToString();
        }

    }
}

So if you have an class that implements iExample

public class SelfParser : iExample
{
    public iExample Parse(string value)
    {
        return new SelfParser();
    }
}

You will be able to use it like this:

abc<SelfParser> abcInstance = new abc<SelfParser>();
abcInstance.a = "useless text";
string unParsed = abcInstance.a; // Will return "SelfParser"
Community
  • 1
  • 1
Oleg Grishko
  • 4,132
  • 2
  • 38
  • 52
1

T.parse is not known in the generic parameter. You have to make it known. dont use reflection. Its slow and generally a bad solution in this case. use generics in a correct way.

You have to specify that T can be only classes which implement an interface which contains a parse method:

        class abs<T> where T : IParsable<T>
        {
        //your implementation here
        }

        interface IParsable<T>
        {
           T Parse(string value);
        }

        public class Specific : IParsable<Specific>
        {
            public Specific Parse(string value)
            {
                throw new NotImplementedException();
            }
        }
fixagon
  • 5,506
  • 22
  • 26
0

While you can't do exactly that with Generics (there are no type constraints for that enforce a specific method signature, only struct/object/interface constraints).

You can create a base class whose constructor takes the Parse method. See my Int32 implementation at the bottom.

class MyParseBase <T>
{
  public MyBase (Func<string,T> parseMethod)
  {
     if (parseMethod == null)
        throw new ArgumentNullException("parseMethod");
     m_parseMethod = parseMethod;
  }
  private T foo; 
  public string a {
    set
    {
       foo = m_parseMethod(value);
    }
    get
    {
       return foo.toString();
    }
  }
 }

class IntParse : MyParseBase<Int32>
{
    public IntParse()
       : base (Int32.Parse)
    {}
}
agent-j
  • 27,335
  • 5
  • 52
  • 79
0

This is a variation on Oleg G's answer which removes the need for the new() type constraint. The idea is you make a Parser for each type you want to be contained in an abs, and inject it - this is a formalization of the Func<string, T> approach as well.

interface IParser<T>
{
    T Parse(string value);
}    

class abs<T> 
{
    private readonly IParser<T> _parser;
    private T foo; 

    public abs(IParser<T> parser) 
    { 
      _parser = parser; 
    }

    public string a {
    set 
    {
      foo = _parser.Parse(value);
    }
    get
    {
      return foo.ToString();
    }     
}
Community
  • 1
  • 1
Paul Phillips
  • 6,093
  • 24
  • 34
0
class abc<T> {
  private T foo; 
  public string a {
    set {
      var x_type = typeof(T);
      foo = (T)x_type.InvokeMember("Parse", System.Reflection.BindingFlags.InvokeMethod, null, value, new []{value});
    }

    get{
       return foo.ToString();
    }
  }
}
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70