how to reference generic objects if you can't define the type

39 Views Asked by At

I'm trying to make a custom config solution and needs to be able to add services as transient, but I can't figuer out how to reference a generic element when I don't know the type.

I add an element of type Transient which takes multiple TransientItem elements. Later I wan't to iterate the items and add them to the service collection.

Sounds easy, but I must be missing something.

public interface ITransient : IAppFeatureBase
{
    IEnumerable<TransientItem<????>> Get();
}

public sealed class Transient : AppFeatureBase, ITransient
{
    private readonly ICollection<TransientItem<????>> _items = new Collection<TransientItem<?????>>();

    public Transient(params TransientItem<?????>[] items) : base(Features.Transient)
    {
        _items.AddRange(_items);
    }

    public IEnumerable<TransientItem<????>> Get() => _items;
}

public struct TransientItem<TEntity> where TEntity : class
{
    public readonly IServiceCollection Add(IServiceCollection services) => services.AddTransient<TEntity>();
}

public struct TransientItem<TInterface, TEntity> where TInterface : class
                                                    where TEntity : class, TInterface
{
    public readonly IServiceCollection Add(IServiceCollection services) => services.AddTransient<TInterface, TEntity>();
}

This is where I configure the services:

foreach(Transient trans in features.Where(feat => feat.Type == Features.Transient))
{
    foreach(TransientItem<????> item in trans.Get())
    {
        item.Add(builder.Services);
    }
}

Thanks!

1

There are 1 best solutions below

2
Enigmativity On BEST ANSWER

The bottom-line is you can't do what you're asking because constructors can't have generic elements added unless the class is generic.

But, based on the comments, it sounds like you're just trying to create a service locator container that you can register a concrete instance to an interface and in another piece of your code pull out that concrete instance based on the interface.

Here's the kind of code I do for that:

public sealed class Registry 
{
    private readonly Dictionary<Type, object> _items = new Dictionary<Type, object>();

    public void Register<I>(I instance)
    {
        _items[typeof(I)] = instance;
    }

    public I Resolve<I>() => (I)_items[typeof(I)];

    public bool TryResolve<I>(out I instance)
    {
        if (_items.ContainsKey(typeof(I)))
        {
            instance = (I)_items[typeof(I)];
            return true;
        }
        instance = default(I);
        return false;
    }
}

So, now, I have these interfaces and classes:

public interface IFoo { }
public class Foo : IFoo { }

public interface IBar { }
public class Bar : IBar { }

I can use them like this:

var registry = new Registry();
registry.Register<IFoo>(new Foo());
registry.Register<IBar>(new Bar());

/* somewhere else in the code */

IFoo foo = registry.Resolve<IFoo>();

if (registry.TryResolve<IBar>(out IBar bar))
{
    /* do something with `bar` */
}