0

In specflow there is a class called Table which has a method Table.CreateInstance<T>(). T basically is a class whose properties will get automatically mapped with contents of the table.

I have multiple classes that T needs to be mapped to based on className that gets passed as a string. Example:

public void ThenTheFollowingFieldsArePopulated(Table table, string className)
{
    Type myType = Type.GetType($"abcd.Data.SecType.{className}, abcd.Data");
    var obj = Activator.CreateInstance(myType);
    var basicOrder = table.CreateInstance<obj>();
}

I am successfully populating both myType and obj. But... I get a compilation error as:

"obj is a variable but used as a type".

Please could some one help on a fix for this?

Magnetron
  • 7,495
  • 1
  • 25
  • 41
makil
  • 489
  • 2
  • 7
  • 19
  • That's not how generics work. Would you declare a variable or a method parameter of type `obj`? Of course not. You would specify a data type. That's what you need to specify for a generic type parameter too. What you want to do may seem convenient but it makes no sense if you understand how generics work. – user18387401 Aug 10 '22 at 10:59
  • Why would you make the class name part of the test definition? That makes very little sense. `CreateInstance` exists so you can take a general, untyped `Table` (as you'd specify in the tests) and then get a specific instance of a type to actually implement the tests with. Different scenarios should arguably be using different (known) types, otherwise you're running the risk of writing extremely generic tests that are better off implemented with a regular test framework instead of Specflow. All that said -- `MakeGenericMethod` is your friend for invoking generic methods with a `Type`. – Jeroen Mostert Aug 10 '22 at 10:59
  • What is table? You can create an instance like `Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(string)))`. If you've a Method you can use reflection to get the `MethodInfo` and using `MakeGenericMethod` and invoke them. – Sebastian Schumann Aug 10 '22 at 11:01
  • Thanks JeroenMostert and Sebastian. Table is a class under TechTalk.SpecFlow basicaly containing rows and columns. The method createinstance automatically maps the values of the Table to properties of classNam. Acts a bit like an out of the box autmapper. – makil Aug 10 '22 at 11:23
  • CreateInstance only maps the values of the properties that exists in the class. The properties change with sec types but the steps to achieve it are exactly the same. Hence I was thinking of not having 100 if else condition and instead I can use reflection to get the class type and pass it as T. – makil Aug 10 '22 at 11:26
  • @user18387401 not sure if I understood any of what you said mate, sorry. – makil Aug 10 '22 at 11:27
  • Would you expect to be able to declare a variable of type `obj`? E.g. `obj myVar = null;`. Yes or no? If not, why not? That's the answer to your current issue. – user18387401 Aug 10 '22 at 11:40
  • right if we can leave obj for a second, why cant I use Type myType = Type.GetType($"abcd.Data.SecType.{className}, abcd.Data"); var basicOrder = table.CreateInstance(); I can see myType being populated and of the type I want but I cannot use it in generic context. Is there a way to achieve this? I am basically doing a bit of redesign based on Jeroen and Sebastians answer but I was wondering if this is even possible? – makil Aug 10 '22 at 11:49
  • 1
    Yes, through `MakeGenericMethod`, as already stated. See [this](https://stackoverflow.com/q/232535/4137916). `Table.CreateInstance` is actually an extension method with overloads, and so has to be invoked using `typeof(TableHelperExtensionMethods).GetMethod(nameof(TableHelperExtensionMethods.CreateInstance), new[] { typeof(Table) }).MakeGenericMethod(myType).Invoke(null, new[] { table });`. Quite a mouthful. – Jeroen Mostert Aug 10 '22 at 12:08
  • @JeroenMostert, legend!! If you can add that as the answer, I can then accept this as the accepted answer. – makil Aug 10 '22 at 14:56

0 Answers0