简体   繁体   中英

Is it possible to create a Func that takes T as runtime parameter?

Is it possible to create a Func that has a generic T as runtime parameter?

I'm using Func to create some instances that are using generics. I would like to do something like this:

var myFunc = new Func<IEnumerable<T>>(x => new List<T>());
IEnumerable<string> result = myFunc<string>();
IEnumerable<int> result2 = myFunc<int>();
IEnumerable<Bar> result3 = myFunc<Bar>();

But it obviously doesn't compile because T is unknown at this moment. The code below will compile, but it will not work in my case since I need the strongly typed List<T> .

var func = new Func<IList>(() => new ArrayList());

If it's not possible with Func , is it possible with Expression or delegates ?

For background info I can say that I'm using this as part of a factory pattern together with Unity IoC. I'm building on this example: Unity auto-factory with params .

EDIT: I realise that my question was poorly asked. Sorry for that. I tried to keep the question simple by not including the background (purpose). The whole point is that I'm using Unity as IoC and have a factory that looks like this:

public interface ICollectionWrapperFactory
{
    ICollectionWrapper<T> CreateCollection<T>(IEnumerable<T> items);
}

public class CollectionWrapperFactory :ICollectionWrapperFactory
{
    private readonly IUnityContainer _container;

    public CollectionWrapperFactory(IUnityContainer container)
    {
        _container = container;
    }

    public ICollectionWrapper<T> CreateCollection<T>(IEnumerable<T> items)
    {
        ICollectionWrapper<T> collectionWrapper;
        if (items == null)
        {
            collectionWrapper = _container.Resolve<ICollectionWrapper<T>>(new ParameterOverride("items", new T[0]));
        }
        else
        {
            collectionWrapper = _container.Resolve<ICollectionWrapper<T>>(new ParameterOverride("items", items));
        }
        return collectionWrapper;
    }
}

It is using the IUnityContainer to resolve the instance. But I want to remove the dependency to IUnityContainer from the implementation because of the service locator anti-pattern. Instead I want to do something like this:

public class CollectionWrapperFactory :ICollectionWrapperFactory
{
    private readonly Func<IEnumerable<T>, ICollectionWrapper<T>> _createFunc;

    public CollectionWrapperFactory(Func<IEnumerable<T>, ICollectionWrapper<T>> createFunc)
    {
        _createFunc = createFunc;
    }

    public ICollectionWrapper<T> CreateCollection<T>(IEnumerable<T> items)
    {
        ICollectionWrapper<T> collectionWrapper;
        if (items == null)
        {
            collectionWrapper = _createFunc(new T[0]);
        }
        else
        {
            collectionWrapper = _createFunc(items);
        }
        return collectionWrapper;
    }
}

public class CollectionWrapper<TModel> : ICollectionWrapper<TModel>
{
    private IEnumerable<TModel> _items;

    public CollectionWrapper(IEnumerable<TModel> items)
    {
        _items = items;
        TotalCount = items.Count();
    }
    public int TotalCount { get; set; }

    public IEnumerable<TModel> Items
    {
        get { return _items; }
        set { _items = value; }
    }
}

And in my registration:

   container.RegisterType<ICollectionWrapperFactory, CollectionWrapperFactory>(
        new ContainerControlledLifetimeManager(),
        new InjectionConstructor(
            new Func<IEnumerable<T>, ICollectionWrapper<T>>(
                items => container.Resolve<ICollectionWrapper<T>>(new ParameterOverride("items", items)))));

container.RegisterType(typeof(ICollectionWrapper<>), typeof(CollectionWrapper<>), new TransientLifetimeManager());

This way I will remove the dependency to IUnityContainer from the implementation and thus the service locator anti-pattern (I guess?). Instead I need to inject the func to create the instance, but I don't know how to inject a func with a generic parameter.

Good examples for exactly what I want, but with generics:

Factory pattern without service locator

Unity auto-factory with params

You can wrap that in method.

public static class MyFactories {
    public static Func<IEnumerable<T>> CreateListFactory<T>() {
        return () => new List<T>();
    }
}
// ... somewhere else
var myFunc = MyFactories.CreateListFactory<string>();
var result = myFunc(); // it will be List<string>

EDIT: If you want declare generic fields/properties, you must specify the generic parameter in class.

public class CollectionWrapperFactory<T> : ICollectionWrapperFactory
{
    private readonly Func<IEnumerable<T>, ICollectionWrapper<T>> _createFunc;

    public CollectionWrapperFactory(Func<IEnumerable<T>, ICollectionWrapper<T>> createFunc)
    {
        _createFunc = createFunc;
    }

    public ICollectionWrapper<T> CreateCollection(IEnumerable<T> items)
    {
        ICollectionWrapper<T> collectionWrapper;
        if (items == null)
        {
            collectionWrapper = _createFunc(new T[0]);
        }
        else
        {
            collectionWrapper = _createFunc(items);
        }
        return collectionWrapper;
    }
}

You can use a simple method as a factory.

Func<IEnumerable<T>> CreateFunc<T>() {
    return () => new List<T>();
}

// Usage
var func = CreateFunc<string>();
var list = func();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM