简体   繁体   中英

Fulfilling a new() constraint on a generic method when using System.Object

I'm working with two third-party libraries, and racking my brain over a way to solve this particular issue. I'm implementing an interface which will pass me objects of type object , and in a good number of the calls, I need to pass them into a generic method that expects a type with class and new() constraints defined.

I know that any object I pass through will meet these constraints, but as far as I can see, there's no simple way I can specify to a generic method that an object satisfies these criteria. An interface can't specify constraints on constructors, and an abstract class isn't permitted as a type argument to the generic method.

The objects being passed in in this case are known and controlled by me, but the signature of both Delete methods can't be modified. Ideally I'd be able to just implement an interface guaranteeing the parameterless constructor criteria, but that doesn't seem to be an option.

Here's an example of what I'm talking about:

public void Delete(object toDelete) {
    _repo.Delete(toDelete)  // signature here is _repo.Delete<T>(T obj) where T : class, new()
}

To give some background and hopefully explain things - the "Delete" call is an implementation of IUpdatable in ADO.NET Data services, while the _repo.Delete<T> call is from SubSonic . I have one DataContextProvider class that will be handling these requests (and other similar ones) for every class that I'm exposing through my model, so a direct cast to a specific class isn't feasible. I can guarantee that the classes always are classes and have a parameterless constructor, but I cannot say from the DataContext that there are only a fixed set of classes that may be passed down - ideally I'd like the DataContext to work with new classes without modification.

You need to use reflection, you need to get the MethodInfo object for the method to call on your repository, and invoke it dynamically.

Note that there could be a much simpler way to do this, but it sounds to me that you have tried this, otherwise it would be the obvious solution.

Since your "_repo" variable must be of a specific type, like Repository<Employee> , you could perhaps just cast it, like this?

_repo.Delete(toDelete as Employee);
// or
_repo.Delete((Employee)toDelete);

or, if it's generic:

_repo.Delete(toDelete as T);

If that's not an option, due to code you haven't shown, you need to resort to reflection.

Here's the example:

using System;
using System.Reflection;

namespace ConsoleApplication14
{
    public class Program
    {
        static void Main(string[] args)
        {
            Dummy d = new Dummy();
            EntityType e = new EntityType();
            d.Delete(e);

            Console.In.ReadLine();
        }
    }

    public class EntityType
    {
        public EntityType()
        {
        }
    }

    public class Dummy
    {
        private Repository<EntityType> _repo = new Repository<EntityType>();

        public void Delete(object toDelete)
        {
            Type t = _repo.GetType();
            Type genericType = t.GetGenericArguments()[0];
            MethodInfo mi = t.GetMethod("Delete",
                BindingFlags.Public | BindingFlags.Instance,
                null, new Type[] { genericType }, new ParameterModifier[0]);

            // _repo.Delete(toDelete);
            mi.Invoke(_repo, new Object[] { toDelete });
        }
    }

    public class Repository<T>
        where T: class, new()
    {
        public void Delete(T value)
        {
            Console.Out.WriteLine("deleted: " + value);
        }
    }
}

I know that any object I pass through will meet these constraints

Its a public method. You don't have to worry about any object that you pass in. You have to worry about any object that anyone passes in.

If you know that the object meets those constraints, do you also know what type the object is? If so, then just cast it to that type.

I suppose that this is repeating what you already know, but if you really can't alter either of those Delete methods then you're stuck.

The outer method takes a plain object as an argument and the inner method requires a T argument where T meets the class, new() constraint. Unfortunately, there's no way that those two can be reconciled without alterations.

You can use reflection with the GetConstructors method to verify that the type has a parameterless constructor. Is that what you mean by new() constraints?

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