1

I have this method that creates an instance of a class, measures the time it takes to load the class, and has the option to call a method. I'm currently trying to implement the option to pass parameters to the constructor of this method (IE T) but I'm a bit stuck, can anyone help?

public static T CreateInstanceOf<T>(Action<T> configure = null) where T : new()
{
    var stopwatch = Stopwatch.StartNew();

    var result = new T();

    configure?.Invoke(result);

    stopwatch.Stop();

    Logger.Trace("Loaded " + result.GetType().Name + " [took " + stopwatch.ElapsedMilliseconds + "ms]");

    return result;
}
kogop
  • 83
  • 2
  • 2
    I'd probably start with [typeof(T).GetConstructors()](https://msdn.microsoft.com/en-us/library/e687hf0d(v=vs.110).aspx) so you can determine what each constructor's parameters and associated types are. Then you can use [Activator.CreateInstance()](https://msdn.microsoft.com/en-us/library/wcxyzt4d(v=vs.110).aspx) to actually create the object. – itsme86 Feb 16 '18 at 00:44
  • There's no guaranteeing through templates (or interfaces) that any non-default constructors exist. So you have to use reflection. And your function should succeed (or fail very gracefully) as long as the default constructor exists. The comment from @itsme86 is a more specific (and probably helpful) version of this advice. – zzxyz Feb 16 '18 at 00:46
  • Possible duplicate of [Passing arguments to C# generic new() of templated type](https://stackoverflow.com/questions/840261/passing-arguments-to-c-sharp-generic-new-of-templated-type) – Stephen Kennedy Feb 16 '18 at 14:39

2 Answers2

1

It's much easier if you keep the work of creating class instances out of this method, since the purpose isn't really to create objects - you just want to time the constructors. You could write your method like this instead:

public T TimeCreationOf<T>(Func<T> creator, Action<T> configure = null)
{
    var stopwatch = Stopwatch.StartNew();

    var result = creator.Invoke();

    configure?.Invoke(result);

    stopwatch.Stop();

    Logger.Trace("Loaded " + result.GetType().Name + " [took " + stopwatch.ElapsedMilliseconds + "ms]");

    return result;
}

(Since you're just timing, do you need this method to return the instance of T that was created?)

Now what you pass into this method is a function that returns a T. And since what you want to time is the constructor, you can just pass in a function that calls the constructor and returns an instance of T.

This makes it much easier because you don't need the new() constraint - T could be any type with any sort of constructor. And you don't need to worry about how the "timing" method will call the constructors - the functions you're passing in do that for you:

TimeCreationOf(() => new ClassOne());
TimeCreationOf(() => new ClassTwo(5, "X"));

Because you're calling the constructors directly you know exactly which constructor you want to call and what to pass to it. If you tried to write a generic method that could create all sorts of objects it would be really difficult.

And if you still need to pass in another Action<T> that performs some additional configuration on the object (and you want to include that in the timing) you can do that as well.

TimeCreationOf(() => new ClassTwo(), c =>
{
    c.SomeProperty = "x";
    c.DoSomethingElse();
});
Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
0

T is not a method - it's a generic type, that you have required have a parameterless constructor, which you are calling. Since there's not a syntax to specify that the type implement a specific constructor signature, you're left with two options:

  1. Use reflection (e.g. Activator.CreateInstance) to pass parameters to the constructor and hope that whatever T is at runtime supports those parameters, or
  2. Require that T implement some interface that you can use to initialize the object:

    public interface IInitializable
    {
        public void Init(...parameters...)
    }
    public static T CreateInstanceOf<T>(Action<T> configure = null) where T : new(), IInitializable
    {
          T = new T();
          T.Initialize(...parameters...);
          ...
    }
    
D Stanley
  • 149,601
  • 11
  • 178
  • 240