简体   繁体   English

检查类是否实现了泛型接口

[英]Checking Whether Class Implements Generic Interface

I'm using the following generic function to determine whether a class implements a specified interface:我正在使用以下通用函数来确定一个类是否实现了指定的接口:

private static bool HasFieldType<TEntity, TInterface>()
{
    return typeof(TInterface).IsAssignableFrom(typeof(TEntity));
}

This works fine for the majority of the time.这在大多数情况下都可以正常工作。

However, I now have an interface which has a generic parameter:但是,我现在有一个具有通用参数的接口:

public interface IStatusField<TEnum> where TEnum : System.Enum
{
    TEnum Status { get; set; }
}

And this causes the HasFieldType function to break with an unexpected use of unbound generic name error.这会导致HasFieldType函数因unexpected use of unbound generic name错误而中断。

Ideally, I want to call the function like:理想情况下,我想调用如下函数:

if (HasFieldType<TEntity, IStatusField<>>()) 
{
    // builder is an EntityTypeBuilder instance
    builder.Property("Status")
        .HasMaxLength(255)
        .HasConversion(new EnumToStringConverter<>());
}

But this won't work as I'm not specifying the generic type for both the IStatusField<> or the EnumToStringConverter<> .但这不起作用,因为我没有为IStatusField<>EnumToStringConverter<>指定泛型类型。

Is there any way around this?有没有办法解决?

UPDATE更新

This code forms part of a generic base IEntityTypeConfiguration class as follows:此代码构成通用基础IEntityTypeConfiguration类的一部分,如下所示:

public abstract class EntityTypeConfiguration<TPrimaryKey, TEntity> : IEntityTypeConfiguration<TEntity> where TEntity : Entity<TPrimaryKey>
{
    public void Configure(EntityTypeBuilder<TEntity> builder)
    {
        builder.HasKey(e => e.Id);

        builder.Property(e => e.Id)
            .IsRequired()
            .HasMaxLength(13)
            .HasValueGenerator<PrimaryKeyValueGenerator>();

        // Apply the generic interface properties
        builder.ApplyInterfaceFields<TPrimaryKey, TEntity>();

        // Apply any additional configuration
        this.OnConfigure(builder);
    }

    protected abstract void OnConfigure(EntityTypeBuilder<TEntity> builder);
}

// In an extension class, I have
public static void ApplyInterfaceFields<TPrimaryKey, TEntity>(this EntityTypeBuilder<TEntity> builder) where TEntity : Entity<TPrimaryKey>
{
    // Check other implementations (removed for brevity)

    // IStatusField implementation
    if (HasFieldType<TEntity, IStatusField<>())
    {
        builder.Property("Status")
            .HasMaxLength(255)
            .HasConversion(new EnumToStringConverter<>());
    }

}

At the point of checking for IStatusField implementation, I know nothing about the generic type specified.在检查IStatusField实现时,我对指定的泛型类型一无所知。 I think this may be the bigger problem...我认为这可能是更大的问题......

Ok, so I've managed to punch my way around the problem.好的,所以我设法解决了这个问题。

It needs a bit of tidying and some error checking but in the crudest form:它需要一些整理和一些错误检查,但以最粗略的形式:

private static bool HasFieldType<TEntity>(Type interfaceType)
{
    var interfaces = typeof(TEntity).GetTypeInfo().ImplementedInterfaces;

    // Iterate through the interfaces
    foreach (var intf in interfaces)
    {
        // Compare interface names
        if (intf.FullName.Contains(interfaceType.FullName))
        {
            return intf.IsAssignableFrom(typeof(TEntity));
        }
    }

    return false;
}

Which now enables this to work:现在使它能够工作:

// IStatusField implementation
if (HasFieldType<TEntity>(typeof(IStatusField<>)))
{
    builder.Property("Status")
        .HasMaxLength(255)
        .HasConversion<string>();
}

I can just use the built-in automatic string-to-enum conversions from EF to do the grunt work.我可以使用 EF 的内置自动字符串到枚举转换来完成繁重的工作。

Instead of trying to wrestle with resolving generic type arguments from nothing, you might consider approaching it from the opposite direction, by getting a list of interfaces implemented by TEntity , filtering it to search for an IStatusField .与其尝试从无到有解析泛型类型参数,您可以考虑从相反的方向接近它,通过获取由TEntity实现的接口列表,过滤它以搜索IStatusField Once you've located the field, you can get its' generic type arguments and pass those to your EnumToStringConverter :找到该字段后,您可以获取其通用类型参数并将它们传递给您的EnumToStringConverter

var statusField = typeof(TEntity)
    .GetInterfaces()
    .FirstOrDefault(x => x.Name.StartsWith("IStatusField"));

Value given TEntity : IStatusField<ConsoleColor> :给定值TEntity : IStatusField<ConsoleColor> :

statusField.GenericTypeArguments = [ typeof(System.Color) ]

From there though you're not done;从那里虽然你还没有完成; you must still construct an instance of the generic type EnumToStringConverter<System.Color> .您仍然必须构造泛型类型EnumToStringConverter<System.Color>的实例。 This is rather simple and outlined here .这相当简单,并在此处进行了概述。

Edit: I realized that because you'd be invoking a constructor, it's not quite the same.编辑:我意识到,因为你会调用构造函数,这是不太一样的。 Here's how you'd accomplish this:以下是您如何完成此操作:

var statusField = typeof(TEntity)
            .GetInterfaces()
            .FirstOrDefault(x => x.Name.StartsWith("IStatusField"));

        if (statusField != null)
        {

            var enumType = statusField.GenericTypeArguments[0]; // get the IStatusField<T> value

            // get the default constructor after supplying generic type arg
            var converterType = typeof(EnumToStringConverter<>)
                .MakeGenericType(enumType)
                .GetConstructors()[0];

            // invoke the constructor. Note the null (optional) param
            dynamic converter = converterType.Invoke(new Object[1]);

                builder.Property("Status")
                    .HasMaxLength(255)
                    .HasConversion(converter);
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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