Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 通过外部功能约束泛型类型_C#_.net_Generics_Dynamic_Interface - Fatal编程技术网

C# 通过外部功能约束泛型类型

C# 通过外部功能约束泛型类型,c#,.net,generics,dynamic,interface,C#,.net,Generics,Dynamic,Interface,背景: 我与一家组织合作,该组织拥有不断增长的数据类型集合,他们需要从中提取数据。我无法更改这些数据类型。有些是由其他组织提供的XML文件机器生成的;一些由不妥协的内部开发团队控制;还有一些是如此古老,以至于没有人愿意以任何方式改变它们,因为害怕它会破坏整个地球的稳定,导致它撞向太阳。这些类不共享任何公共接口,也不派生自除对象以外的任何公共类型。以下给出了几个示例类以供说明: public class Untouchable { public string Dat

背景:

我与一家组织合作,该组织拥有不断增长的数据类型集合,他们需要从中提取数据。我无法更改这些数据类型。有些是由其他组织提供的XML文件机器生成的;一些由不妥协的内部开发团队控制;还有一些是如此古老,以至于没有人愿意以任何方式改变它们,因为害怕它会破坏整个地球的稳定,导致它撞向太阳。这些类不共享任何公共接口,也不派生自除
对象
以外的任何公共类型。以下给出了几个示例类以供说明:

    public class Untouchable
    {
        public string Data;
    }

    public class Unchangeable
    {
        public int Info;
    }
好消息是,在大多数情况下,我可以使用固定功能从各种类的实例中获取至少一些数据。不幸的是,这些类中的大多数也有奇怪的专门数据,需要特定于类的逻辑来从中提取数据。此外,信息通常需要保存在数据提取器内部,因为我从中提取数据的数据对象具有“交互”(不要问)

我创建了一个抽象的泛型
提取器
类作为通用方法的存储库,以及一个
IExtractor
接口作为访问功能的方便句柄。我还有一些此类的特定(非泛型?)实现,它们可以从一些数据类型构建的业务对象中提取信息。下面是一些示例代码来说明:

    public interface IExtractor<T>
    {
        string ExtractionOne(T something);
        string ExtractionTwo(T something);
    }

    public abstract class Extractor<T> : IExtractor<T>
    {
        private string internalInfo; // Certain business logic requires us to keep internal info over multiple objects that we extract data from.
        protected Extractor() { internalInfo="stuff"; }

        public virtual string ExtractionOne(T something)
        {
            return "This manipulation is generally applicable to most conceivable types.";
        }

        public abstract string ExtractionTwo(T something); // This DEFINITELY needs to be overridden for each class T
    }

    public class UntouchableExtractor : Extractor<Untouchable>
    {
        public UntouchableExtractor() : base() { }

        public override string ExtractionTwo(Untouchable something)
        {
            return something.Data;
        }
    }

    public class UnchangeableExtractor : Extractor<Unchangeable>
    {
        public UnchangeableExtractor() : base() { }

        public override string ExtractionTwo(Unchangeable something)
        {
            return something.Info.ToString();
        }
    }
公共接口IExtractor
{
弦提取酮(T某物);
字符串提取2(T某物);
}
公共抽象类提取器:IExtractor
{
私有字符串internalInfo;//某些业务逻辑要求我们在从中提取数据的多个对象上保留内部信息。
受保护的提取器(){internalInfo=“stuff”;}
公共虚拟字符串提取ONE(T)
{
return“此操作通常适用于大多数可能的类型。”;
}
公共抽象字符串ExtractionTwo(T something);//这肯定需要为每个类T重写
}
公共类非接触提取器:提取器
{
公共非接触提取器():base(){}
公共重写字符串提取2(不可触及的内容)
{
返回某物。数据;
}
}
公共类不可更改提取器:提取器
{
公共不可更改提取器():base(){}
公共重写字符串提取2(不可更改的内容)
{
返回something.Info.ToString();
}
}
我还不支持所有可用的数据类型,但管理层希望使用命令行界面向最终用户推出数据提取器。他们告诉我,我们应该开始提取我们可以提取的数据,然后再进行其他工作。在时间允许的情况下,我和其他程序员将添加对许多不可修改类型的支持,最终用户有望解决我们的延迟问题。这在我们的现实环境中是有意义的,所以就这么做吧

问题:

我们要从中提取信息的数据类型列表非常大。在代码中维护受支持类型的显式列表将是一件棘手且容易出错的事情——特别是如果我们发现特定数据提取器存在任何问题,并且需要撤销支持,直到我们修复了一些bug

我想从一个入口点支持大量不断变化的受支持数据类型列表,该入口点根据传入的
动态数据对象
动态确定要使用的
IExtractor
的“正确版本”。如果没有实现
IExtractor
以支持给定
dataObject
的类,则应抛出错误

什么不起作用:

我尝试使用
动态对象
并使用
类型(提取器)。MakeGenericType(thing.GetType())
创建
提取器
提取器
的实例,但这些都是抽象类,因此我无法使用
Activator.CreateInstance()
来构建这些类的实例。这种方法的核心问题是,不幸的是,它正在寻找表单
提取器的,而不是表单
IExtractor的接口

我曾考虑在某个类中使用扩展方法,如
IExtractor BuildExtractor(this T something)
,但我担心遇到一些称为
BuildExtractor
的业务逻辑,这些逻辑已经存在于这些不可接触的类中。这可能是一种不健康的偏执狂,但这正是我所处的位置

我需要帮助的地方:


对于如何为不受约束的类集合创建单个入口点,我欢迎任何建议。提前感谢。

下面的代码片段将创建
提取器
类的具体实例,并动态调用该实例的方法

var测试=新的不可更改();
var baseType=typeof(提取器).MakeGenericType(test.GetType());
var extractorType=Assembly.getExecutionGassembly()
.GetTypes().FirstOrDefault(t=>t.IsClass&!t.isasStract&&t.isubClassof(baseType));
if(提取器类型!=null)
{
动态提取器=Activator.CreateInstance(提取器类型);
字符串结果=提取器?提取器2(测试);
}
当然,它是简化的,您可以传递
Unchangeable
Untouchable
类的特定实例,并限制程序集类型扫描(并且只获取一次所有类型)

这里的缺点是您必须注意
ExtractionOne
ExtractionTwo
签名,因为它们是在动态系统上调用的
public static class TypeExt {
    public static bool IsBuiltin(this Type aType) => new[] { "/dotnet/shared/microsoft", "/windows/microsoft.net" }.Any(p => aType.Assembly.CodeBase.ToLowerInvariant().Contains(p));
    public static IEnumerable<Type> ImplementingTypes(this Type interfaceType, bool includeAbstractClasses = false, bool includeStructs = false, bool includeSystemTypes = false, bool includeInterfaces = false) =>
        AppDomain.CurrentDomain.GetAssemblies()
                               .SelectMany(a => a.GetLoadableTypes())
                               .Distinct()
                               .Where(aType => (includeAbstractClasses || !aType.IsAbstract) &&
                                               (includeInterfaces ? aType != interfaceType : !aType.IsInterface) &&
                                               (includeStructs || !aType.IsValueType) &&
                                               (includeSystemTypes || !aType.IsBuiltin()) &&
                                               interfaceType.IsAssignableFrom(aType) &&
                                               aType.GetInterfaces().Contains(interfaceType));
}

public static class AssemblyExt {
    //https://stackoverflow.com/a/29379834/2557128
    public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) {
        if (assembly == null)
            throw new ArgumentNullException("assembly");
        try {
            return assembly.GetTypes();
        } catch (ReflectionTypeLoadException e) {
            return e.Types.Where(t => t != null);
        }
    }

}
public static class TypeExt {
    public static bool IsBuiltin(this Type aType) => new[] { "/dotnet/shared/microsoft", "/windows/microsoft.net" }.Any(p => aType.Assembly.CodeBase.ToLowerInvariant().Contains(p));

    static Dictionary<Type, HashSet<Type>> FoundTypes = null;
    static List<Type> LoadableTypes = null;

    public static void RefreshLoadableTypes() {
        LoadableTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetLoadableTypes()).ToList();
        FoundTypes = new Dictionary<Type, HashSet<Type>>();
    }

    public static IEnumerable<Type> ImplementingTypes(this Type interfaceType, bool includeAbstractClasses = false, bool includeStructs = false, bool includeSystemTypes = false, bool includeInterfaces = false) {
        if (FoundTypes != null && FoundTypes.TryGetValue(interfaceType, out var ft))
            return ft;
        else {
            if (LoadableTypes == null)
                RefreshLoadableTypes();

            var ans = LoadableTypes
                       .Where(aType => (includeAbstractClasses || !aType.IsAbstract) &&
                                       (includeInterfaces ? aType != interfaceType : !aType.IsInterface) &&
                                       (includeStructs || !aType.IsValueType) &&
                                       (includeSystemTypes || !aType.IsBuiltin()) &&
                                       interfaceType.IsAssignableFrom(aType) &&
                                       aType.GetInterfaces().Contains(interfaceType))
                       .ToHashSet();

            FoundTypes[interfaceType] = ans;

            return ans;
        }
    }
}

public static class AssemblyExt {
    //https://stackoverflow.com/a/29379834/2557128
    public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) {
        if (assembly == null)
            throw new ArgumentNullException("assembly");
        try {
            return assembly.GetTypes();
        }
        catch (ReflectionTypeLoadException e) {
            return e.Types.Where(t => t != null);
        }
    }
}
public static class ImplementingFactory {
    public static Type ExtractorType(dynamic anObject) {
        Type oType = anObject.GetType();
        var iType = typeof(IExtractor<>).MakeGenericType(oType);
        var ans = iType.ImplementingTypes().FirstOrDefault();
        if (ans == null)
            throw new Exception($"Unable to find IExtractor<{oType.Name}>");
        else
            return ans;
    }
}