How do you get the type of generic argument from t

2019-05-29 00:03发布

问题:

We are supposed to instantiate our entities through a factory since they are set up differently on the client and server. I want to make sure this is the case but cant quite get it to work.

public interface IEntityFactory
{
    TEntity Create<TEntity>() where TEntity : new();
}

public abstract class Entity
{
    protected Entity()
    {
        VerifyEntityIsCreatedThroughFactory();
    }

    [Conditional("DEBUG")]
    private void VerifyEntityIsCreatedThroughFactory()
    {
        foreach (var methodBase in new StackTrace().GetFrames().Select(x => x.GetMethod()))
        {
            if (!typeof(IEntityFactory).IsAssignableFrom(methodBase.DeclaringType)
                || methodBase.Name != "Create")
                continue;

            // The generic type is TEnitiy but I want the provided type!
            if (methodBase.GetGenericArguments()[0] != GetType())
                Debug.Fail(string.Format("Use factory when creating {0}.", GetType().Name));
        }
    }
}

回答1:

Is possible to address this structurally instead of at runtime? Can you segregate your entities and the factory in a different assembly, then give the entity constructors internal scoping so that only the factory is able to invoke them?



回答2:

The problem is that the factory method type is resolved at runtime, so the method is considered an "open" one. In that case, the generic argument type will return TEntity, as you are seeing.

Unfortunately, (unless I am missing something), the only way to get what type of TEntity is if a closed method is first created using MethodInfo.MakeGenericMethod and then executed, which is of course not likely to be done by your callers.

See this MSDN page for additional details.



回答3:

Thanks for your replies. Setting the constructor to internal is not a possibility as we have multiple assemblies containing entities. Right now I'm leaning towards creating a class where the factory registers the type to create and the constructor checks against the registered type, it's not the solution I would prefer but it will do for now.