3

How to create a generic class that allows types which has constructor taking one string argument and implements ToString and implements Two functions as below.

    class Convert<T>:ConverterBase
        where T:new()
    {

        public override object StringToField(string from)
        {
            try
            {
                return new T(from);
            }
            catch (ArgumentException exception)
            {
                ThrowConvertException(from, exception.Message);
                return null;
            }
        }

        public override string FieldToString(object from)
        {
            return from.ToString();
        }

    }

Note: ConvertBase is a abstract class in FileHelpers csv reader library. I already have classes that corresponds to my fields in csv, didn't want to create seperate Classes that inherit ConvertBase inorder to use with the FileHelpres library.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
yesraaj
  • 46,370
  • 69
  • 194
  • 251

3 Answers3

2

There is no way to use the constructor constraint (new()) to constrain to anything other than an empty constructor. One way to work around this though is to use a lambda expression. For example

class Convert<T>:ConverterBase {
  private Func<string, T> _factory;
  public (Func<string, T> factory) {
    _factory = factory;
  }

  public override object StringToField(string from) {
    try {
      return _factory(from);
    } ...
  }
}

Now I can create instances of Convert<T> and use a lambda expression to forward to the constructor of the type

new Convert<Foo>(s => new Foo(s));

EDIT C# 2.0 implementation since the OP is stuck with 2.0

public delegate TResult Func<T, TResult>(T arg);

new Convert<Foo>(delegate (string s) { return new Foo(s); });
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
0

You can't. You can only create a constraint that enforces, that your class has a parameterless constructor. The constraint for that is new(), as you know.

ToString() is available on every type, because it is defined on Object and every type inherits from Object. However, you can't know or enforce, whether your type provides it's own implementation of ToString().

To make sure, the type parameter implements two specific methods, put them into an interface and constrain the type parameter to that interface and make your class implement that interface.

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
0

Hi there you can create a base class that stores the converter function and implements both members, after that you can inherit from it and only provide the string to object convertion

public abstract class Convert<T>:ConverterBase
{
    private Func<string, T> ConverterFunction {get;set;}

    protected Convert(Func<string, T> converterFunction)
    {
        ConverterFunction = converterFunction;
    }

    public override object StringToField(string from)
    {
        try
        {
            return ConverterFunction(from);
        }
        catch (ArgumentException exception)
        {
            ThrowConvertException(from, exception.Message);
            return null;
        }
    }

    public override string FieldToString(object from)
    {
        return from.ToString();
    }

}

Later create some simple derived classes for example:

public class ConvertMyClass:ConverterBase<MyClass>
{
    public ConvertMyClass()
        :base(from => new MyClass(from)
    { 
    }
}

Hope this helps :) Remember to download the last stable version from: http://teamcity.codebetter.com/viewLog.html?buildId=lastSuccessful&buildTypeId=bt66&tab=artifacts&guest=1

EDIT: Using Reflection you can try:

class Convert<T>:ConverterBase
{

    public override object StringToField(string from)
    {
        try
        {
            return Activator.CreateInstance(typeof(T), from);
        }
        catch (ArgumentException exception)
        {
            ThrowConvertException(from, exception.Message);
            return null;
        }
    }

    public override string FieldToString(object from)
    {
        return from.ToString();
    }

}

NOTE: Reflection is slow, so you must take that into account

Marcos Meli
  • 3,468
  • 24
  • 29
  • My intention is to completely eliminating classes like this ConvertMyClass, provided the Type T has a constructor with one string argument so that I can use `[FieldConverter(typeof(Convert))]` – yesraaj Apr 01 '11 at 03:21
  • @yesraaj: So you need to use reflection to get the constructor with one argument string and dynamicaly call it using Activator, I just updated the anwser – Marcos Meli Apr 01 '11 at 03:51