jpsqr
jpsqr

Reputation: 21

C# how to create a generic type argument

I have existing test code which needs to be extended.

I would like to keep this as generic as possible in order to reduce code duplication, here is a simplified snippet of the current code:


    public class FilterValueOne { }

    [TestClass]
    public class TestClass
    {
        [TestMethod]
        public void FilterValueOne_TestMethod()
        {
            ManipulateData(BuildFilter());
        }

        private void ManipulateData(FilterValueOne filter)
        {
            // Do some stuff with filter
        }

        private FilterValueOne BuildFilter()
        {
            var filter = new FilterValueOne();
            // Initialize filter data members here...
            return filter;
        }
    }

This works, but it is limited to the "FilterValueOne" type.

I want to expand this and make it more generic by implementing a generic type argument.

The code snippet below is what I am after:


    // Enum will expand as the app grows
    public enum TypeEnum
    {
        ValueOne,
        ValueTwo,
        ValueThree
    }

    // More classes will be added as the app grows
    public class FilterValueOne { }
    public class FilterValueTwo { }
    public class FilterValueThree { }

    [TestClass]
    public class TestClassGeneric
    {
        // _filter to be used as the generic type argument
        private object _filter;

        // Constructor
        public TestClassGeneric(TypeEnum val)
        {
           switch (val)
           {
               case TypeEnum.ValueOne:
                   _filter = GetTestObject<FilterValueOne>();
                   break;
               case TypeEnum.ValueTwo:
                   _filter = GetTestObject<FilterValueTwo>();
                   break;
               case TypeEnum.ValueThree:
                   _filter = GetTestObject<FilterValueThree>();
                   break;
               default:
                   _filter = null;
                   break;
           }
        }

        private T GetTestObject<T>()
          where T : class
        {
           // Code simplified
           object returnValue = new object();
           return (T)returnValue;
        }

        [TestMethod]
        public void Generic_FilterValue_TestMethod()
        {
            // Error: The type _filter could not be found. (However it exists)
            ManipulateData(BuildFilter<_filter>());
        }

        private void ManipulateData<T>(T filter)
        {
            // Do some stuff with filter
        }    

        private T BuildFilter<T>()
         where T : class
        {
            // I want filter to be dynamically assigned to whatever _filter was
            // assigned to in the constructor (FilterValueOne/Two/Three), instead 
            // of "FilterValueOne"

            var filter = typeof(T);

            // Initialize filter data members here...

            return filter;
        }
    }

I ultimately want to use "_filter" as a generic type argument in my test method, but I can't get it to work. I receive an error stating that "_filter could not be found".

I have tried multiple ways of using typeof(_filter) etc. but no success.

I don't understand generics and its potential fully, I don't know if this is even possible or if I'm simply missing something.

Upvotes: 0

Views: 564

Answers (2)

Kit
Kit

Reputation: 21739

The T in BuildFilter<T> is a type parameter. You are passing an instance of a type, in your case an instance of object (aka System.Object) not a type.

You simply cannot do that, hence the compiler error (_filter is not a type; it's an instance and that's why the compiler can't find that "type").

What you want to do instead is instantiate a specific BuildFilter<FilterValueOne>, etc. and create tests that test each of these things.

For example

[TestMethod]
public void Generic_FilterValueOne_TestMethod()
{
    ManipulateData(new BuildFilter<FilterValueOne>());
}

It looks like you're using MS Test; AFAIK MS Test does not support generic tests, so you will have to create a test for each type you're interested in.

Upvotes: 2

opewix
opewix

Reputation: 5083

It's easier to use generic test class, but if you prefer to pass filter type through contructor or as a method parameter you can use Activator class.

public class TestClassGeneric<T> where T : new()
{
    public void Generic_FilterValue_TestMethod()
    {
        var filter = new T();
        // ManipulateData(filter);
    }
}

public class TestClassConstructorArg
{
    private readonly Type type;

    public TestClassConstructorArg(Type type)
    {
        this.type = type;
    }

    public void Generic_FilterValue_TestMethod()
    {
        var filter = Activator.CreateInstance(type);
        // var filter = Activator.CreateInstance(type, BindingFlags.CreateInstance, arguments...);
        // ManipulateData(filter);
    }
}

Upvotes: 0

Related Questions