当前位置:  开发笔记 > 编程语言 > 正文

检查类是否派生自泛型类

如何解决《检查类是否派生自泛型类》经验,为你挑选了7个好方法。

我的项目中有一个派生类的泛型类.

public class GenericClass : GenericInterface
{
}

public class Test : GenericClass
{
}

有没有办法找出一个Type对象是否来自GenericClass

t.IsSubclassOf(typeof(GenericClass<>))

不起作用.



1> JaredPar..:

试试这个代码

static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) {
    while (toCheck != null && toCheck != typeof(object)) {
        var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
        if (generic == cur) {
            return true;
        }
        toCheck = toCheck.BaseType;
    }
    return false;
}


这仅适用于具体类型继承...测试用例:bool expected = true; bool actual = Program.IsSubclassOfRawGeneric(typeof(IEnumerable <>),typeof(List )); Assert.AreEqual(预期,实际); //失败了
如果toCheck类型不是类(即接口),则while循环不会中断.这将导致NullReferenceException.
我不得不说,这是一段很好的代码.while循环实现也避免了不必要的递归性能.这是一个优雅而美丽的解决方案.
我已经在我的框架中将此方法添加到我的ReflectionUtils静态类中,并且我还通过在方法中将toCheck定义为Type toCheck = obj.GetType();来将其作为对象的扩展方法进行了调整.给定"this object obj"是第一个参数.
如果toCheck是您正在寻找的通用类型,这也将返回true.
正如@oillio所说,这与IsSubclass的行为不一样,应该包括:if(genericType == toCheck){return false}在第一行
对于**netcore**,使用`toCheck.GetTypeInfo().IsGenericType`和`toCheck.GetTypeInfo().BaseType`而不是`toCheck.IsGenericType`和`toCheck.BaseType`

2> EnocNRoll - ..:

(由于大量重写而重新发布)

JaredPar的代码答案很棒,但是如果您的泛型类型不是基于值类型参数,那么我有一个提示会使它变得不必要.我被挂断了为什么"是"操作员不能工作,所以我也记录了我的实验结果以供将来参考.请加强此答案以进一步提高其清晰度.

小费:

如果你确定你的GenericClass实现继承了一个抽象的非泛型基类,比如GenericClassBase,你可以毫无困难地问同样的问题:

typeof(Test).IsSubclassOf(typeof(GenericClassBase))

IsSubclassOf()

我的测试表明IsSubclassOf()不适用于无参数泛型类型,例如

typeof(GenericClass<>)

而它会合作

typeof(GenericClass)

因此,以下代码适用于GenericClass <>的任何派生,假设您愿意基于SomeType进行测试:

typeof(Test).IsSubclassOf(typeof(GenericClass))

我唯一想象你想要通过GenericClass <>进行测试的是插件框架场景.


关于"是"算子的思考

在设计时,C#不允许使用无参数泛型,因为它们在那时基本上不是完整的CLR类型.因此,您必须使用参数声明泛型变量,这就是"is"运算符对于处理对象非常强大的原因.顺便说一句,"是"运算符也不能评估无参数的泛型类型.

"is"运算符将测试整个继承链,包括接口.

因此,给定任何对象的实例,以下方法将起到作用:

bool IsTypeof(object t)
{
    return (t is T);
}

这有点多余,但我想我会继续为每个人想象它.

特定

var t = new Test();

以下代码行将返回true:

bool test1 = IsTypeof>(t);

bool test2 = IsTypeof>(t);

bool test3 = IsTypeof(t);

另一方面,如果你想要特定于Ge​​nericClass的东西,你可以使它更具体,我想,像这样:

bool IsTypeofGenericClass(object t)
{
    return (t is GenericClass);
}

然后你会像这样测试:

bool test1 = IsTypeofGenericClass(t);


应该注意的是,编译器对.IsSubclassOf(typeof(GenericClass <>))感到非常满意,只是它没有做您希望做的事情。
但是,如果在编译时不知道SomeType怎么办?

3> 小智..:

我研究了一些样本,发现在某些情况下它们缺乏.此版本适用于各种泛型:类型,接口和类型定义.

public static bool InheritsOrImplements(this Type child, Type parent)
{
    parent = ResolveGenericTypeDefinition(parent);

    var currentChild = child.IsGenericType
                           ? child.GetGenericTypeDefinition()
                           : child;

    while (currentChild != typeof (object))
    {
        if (parent == currentChild || HasAnyInterfaces(parent, currentChild))
            return true;

        currentChild = currentChild.BaseType != null
                       && currentChild.BaseType.IsGenericType
                           ? currentChild.BaseType.GetGenericTypeDefinition()
                           : currentChild.BaseType;

        if (currentChild == null)
            return false;
    }
    return false;
}

private static bool HasAnyInterfaces(Type parent, Type child)
{
    return child.GetInterfaces()
        .Any(childInterface =>
        {
            var currentInterface = childInterface.IsGenericType
                ? childInterface.GetGenericTypeDefinition()
                : childInterface;

            return currentInterface == parent;
        });
}

private static Type ResolveGenericTypeDefinition(Type parent)
{
    var shouldUseGenericType = true;
    if (parent.IsGenericType && parent.GetGenericTypeDefinition() != parent)
        shouldUseGenericType = false;

    if (parent.IsGenericType && shouldUseGenericType)
        parent = parent.GetGenericTypeDefinition();
    return parent;
}

以下是单元测试:

protected interface IFooInterface
{
}

protected interface IGenericFooInterface
{
}

protected class FooBase
{
}

protected class FooImplementor
    : FooBase, IFooInterface
{
}

protected class GenericFooBase
    : FooImplementor, IGenericFooInterface
{

}

protected class GenericFooImplementor
    : FooImplementor, IGenericFooInterface
{
}


[Test]
public void Should_inherit_or_implement_non_generic_interface()
{
    Assert.That(typeof(FooImplementor)
        .InheritsOrImplements(typeof(IFooInterface)), Is.True);
}

[Test]
public void Should_inherit_or_implement_generic_interface()
{
    Assert.That(typeof(GenericFooBase)
        .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
}

[Test]
public void Should_inherit_or_implement_generic_interface_by_generic_subclass()
{
    Assert.That(typeof(GenericFooImplementor<>)
        .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
}

[Test]
public void Should_inherit_or_implement_generic_interface_by_generic_subclass_not_caring_about_generic_type_parameter()
{
    Assert.That(new GenericFooImplementor().GetType()
        .InheritsOrImplements(typeof(IGenericFooInterface<>)), Is.True);
}

[Test]
public void Should_not_inherit_or_implement_generic_interface_by_generic_subclass_not_caring_about_generic_type_parameter()
{
    Assert.That(new GenericFooImplementor().GetType()
        .InheritsOrImplements(typeof(IGenericFooInterface)), Is.False);
}

[Test]
public void Should_inherit_or_implement_non_generic_class()
{
    Assert.That(typeof(FooImplementor)
        .InheritsOrImplements(typeof(FooBase)), Is.True);
}

[Test]
public void Should_inherit_or_implement_any_base_type()
{
    Assert.That(typeof(GenericFooImplementor<>)
        .InheritsOrImplements(typeof(FooBase)), Is.True);
}


如果它已经和父母一样,那就回来吧!而且他多次调用GetGenericTypeDef.它只需要调用一次
我对ResolveGenericTypeDefinition方法感到困惑."shouldUseGenericType"变量实际上赋值为:`!parent.IsGenericType || parent.GetGenericTypeDefinition()== parent;`所以你用if语句的扩展替换该变量:`if(parent.IsGenericType && shouldUseGenericType)`你得到`if(parent.IsGenericType &&(!parent.IsGenericType ||) parent.GetGenericTypeDefinition()== parent))`然后缩减为`if(parent.IsGenericType && parent.GetGenericTypeDefinition()== parent))parent = parent.GetGenericTypeDefinition();`
这似乎什么都不做.如果这些类型是类似于`int j = 0;``if(j是一个int && j == 0)``{j = 0; 我的参考等于和值等于混合吗?我以为内存中每个Type只有一个实例,因此指向同一类型的两个变量实际上指向内存中的相同位置.

4> Xav987..:

在我看来,这个实现在更多情况下工作(泛型类和接口有或没有启动参数,无论子和参数的数量):

public static class ReflexionExtension
{
    public static bool IsSubClassOfGeneric(this Type child, Type parent)
    {
        if (child == parent)
            return false;

        if (child.IsSubclassOf(parent))
            return true;

        var parameters = parent.GetGenericArguments();
        var isParameterLessGeneric = !(parameters != null && parameters.Length > 0 &&
            ((parameters[0].Attributes & TypeAttributes.BeforeFieldInit) == TypeAttributes.BeforeFieldInit));

        while (child != null && child != typeof(object))
        {
            var cur = GetFullTypeDefinition(child);
            if (parent == cur || (isParameterLessGeneric && cur.GetInterfaces().Select(i => GetFullTypeDefinition(i)).Contains(GetFullTypeDefinition(parent))))
                return true;
            else if (!isParameterLessGeneric)
                if (GetFullTypeDefinition(parent) == cur && !cur.IsInterface)
                {
                    if (VerifyGenericArguments(GetFullTypeDefinition(parent), cur))
                        if (VerifyGenericArguments(parent, child))
                            return true;
                }
                else
                    foreach (var item in child.GetInterfaces().Where(i => GetFullTypeDefinition(parent) == GetFullTypeDefinition(i)))
                        if (VerifyGenericArguments(parent, item))
                            return true;

            child = child.BaseType;
        }

        return false;
    }

    private static Type GetFullTypeDefinition(Type type)
    {
        return type.IsGenericType ? type.GetGenericTypeDefinition() : type;
    }

    private static bool VerifyGenericArguments(Type parent, Type child)
    {
        Type[] childArguments = child.GetGenericArguments();
        Type[] parentArguments = parent.GetGenericArguments();
        if (childArguments.Length == parentArguments.Length)
            for (int i = 0; i < childArguments.Length; i++)
                if (childArguments[i].Assembly != parentArguments[i].Assembly || childArguments[i].Name != parentArguments[i].Name || childArguments[i].Namespace != parentArguments[i].Namespace)
                    if (!childArguments[i].IsSubclassOf(parentArguments[i]))
                        return false;

        return true;
    }
}

这是我的70 76个测试用例:

[TestMethod]
public void IsSubClassOfGenericTest()
{
    Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(BaseGeneric<>)), " 1");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(WrongBaseGeneric<>)), " 2");
    Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), " 3");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IWrongBaseGeneric<>)), " 4");
    Assert.IsTrue(typeof(IChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), " 5");
    Assert.IsFalse(typeof(IWrongBaseGeneric<>).IsSubClassOfGeneric(typeof(ChildGeneric2<>)), " 6");
    Assert.IsTrue(typeof(ChildGeneric2<>).IsSubClassOfGeneric(typeof(BaseGeneric<>)), " 7");
    Assert.IsTrue(typeof(ChildGeneric2).IsSubClassOfGeneric(typeof(BaseGeneric<>)), " 8");
    Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(BaseGeneric)), " 9");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(WrongBaseGeneric)), "10");
    Assert.IsTrue(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric)), "11");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(IWrongBaseGeneric)), "12");
    Assert.IsTrue(typeof(IChildGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric)), "13");
    Assert.IsFalse(typeof(BaseGeneric).IsSubClassOfGeneric(typeof(ChildGeneric2)), "14");
    Assert.IsTrue(typeof(ChildGeneric2).IsSubClassOfGeneric(typeof(BaseGeneric)), "15");
    Assert.IsFalse(typeof(ChildGeneric).IsSubClassOfGeneric(typeof(ChildGeneric)), "16");
    Assert.IsFalse(typeof(IChildGeneric).IsSubClassOfGeneric(typeof(IChildGeneric)), "17");
    Assert.IsFalse(typeof(IBaseGeneric<>).IsSubClassOfGeneric(typeof(IChildGeneric2<>)), "18");
    Assert.IsTrue(typeof(IChildGeneric2<>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "19");
    Assert.IsTrue(typeof(IChildGeneric2).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "20");
    Assert.IsFalse(typeof(IBaseGeneric).IsSubClassOfGeneric(typeof(IChildGeneric2)), "21");
    Assert.IsTrue(typeof(IChildGeneric2).IsSubClassOfGeneric(typeof(IBaseGeneric)), "22");
    Assert.IsFalse(typeof(IBaseGeneric).IsSubClassOfGeneric(typeof(BaseGeneric)), "23");
    Assert.IsTrue(typeof(BaseGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric)), "24");
    Assert.IsFalse(typeof(IBaseGeneric<>).IsSubClassOfGeneric(typeof(BaseGeneric<>)), "25");
    Assert.IsTrue(typeof(BaseGeneric<>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "26");
    Assert.IsTrue(typeof(BaseGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "27");
    Assert.IsFalse(typeof(IBaseGeneric).IsSubClassOfGeneric(typeof(IBaseGeneric)), "28");
    Assert.IsTrue(typeof(BaseGeneric2).IsSubClassOfGeneric(typeof(IBaseGeneric)), "29");
    Assert.IsFalse(typeof(IBaseGeneric<>).IsSubClassOfGeneric(typeof(BaseGeneric2<>)), "30");
    Assert.IsTrue(typeof(BaseGeneric2<>).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "31");
    Assert.IsTrue(typeof(BaseGeneric2).IsSubClassOfGeneric(typeof(IBaseGeneric<>)), "32");
    Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "33");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(WrongBaseGenericA<,>)), "34");
    Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "35");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IWrongBaseGenericA<,>)), "36");
    Assert.IsTrue(typeof(IChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "37");
    Assert.IsFalse(typeof(IWrongBaseGenericA<,>).IsSubClassOfGeneric(typeof(ChildGenericA2<,>)), "38");
    Assert.IsTrue(typeof(ChildGenericA2<,>).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "39");
    Assert.IsTrue(typeof(ChildGenericA2).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "40");
    Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(BaseGenericA)), "41");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(WrongBaseGenericA)), "42");
    Assert.IsTrue(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA)), "43");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(IWrongBaseGenericA)), "44");
    Assert.IsTrue(typeof(IChildGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA)), "45");
    Assert.IsFalse(typeof(BaseGenericA).IsSubClassOfGeneric(typeof(ChildGenericA2)), "46");
    Assert.IsTrue(typeof(ChildGenericA2).IsSubClassOfGeneric(typeof(BaseGenericA)), "47");
    Assert.IsFalse(typeof(ChildGenericA).IsSubClassOfGeneric(typeof(ChildGenericA)), "48");
    Assert.IsFalse(typeof(IChildGenericA).IsSubClassOfGeneric(typeof(IChildGenericA)), "49");
    Assert.IsFalse(typeof(IBaseGenericA<,>).IsSubClassOfGeneric(typeof(IChildGenericA2<,>)), "50");
    Assert.IsTrue(typeof(IChildGenericA2<,>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "51");
    Assert.IsTrue(typeof(IChildGenericA2).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "52");
    Assert.IsFalse(typeof(IBaseGenericA).IsSubClassOfGeneric(typeof(IChildGenericA2)), "53");
    Assert.IsTrue(typeof(IChildGenericA2).IsSubClassOfGeneric(typeof(IBaseGenericA)), "54");
    Assert.IsFalse(typeof(IBaseGenericA).IsSubClassOfGeneric(typeof(BaseGenericA)), "55");
    Assert.IsTrue(typeof(BaseGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA)), "56");
    Assert.IsFalse(typeof(IBaseGenericA<,>).IsSubClassOfGeneric(typeof(BaseGenericA<,>)), "57");
    Assert.IsTrue(typeof(BaseGenericA<,>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "58");
    Assert.IsTrue(typeof(BaseGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "59");
    Assert.IsFalse(typeof(IBaseGenericA).IsSubClassOfGeneric(typeof(IBaseGenericA)), "60");
    Assert.IsTrue(typeof(BaseGenericA2).IsSubClassOfGeneric(typeof(IBaseGenericA)), "61");
    Assert.IsFalse(typeof(IBaseGenericA<,>).IsSubClassOfGeneric(typeof(BaseGenericA2<,>)), "62");
    Assert.IsTrue(typeof(BaseGenericA2<,>).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "63");
    Assert.IsTrue(typeof(BaseGenericA2).IsSubClassOfGeneric(typeof(IBaseGenericA<,>)), "64");
    Assert.IsFalse(typeof(BaseGenericA2).IsSubClassOfGeneric(typeof(IBaseGenericA)), "65");
    Assert.IsFalse(typeof(BaseGenericA).IsSubClassOfGeneric(typeof(ChildGenericA2)), "66");
    Assert.IsFalse(typeof(BaseGenericA2).IsSubClassOfGeneric(typeof(BaseGenericA)), "67");
    Assert.IsTrue(typeof(ChildGenericA3).IsSubClassOfGeneric(typeof(BaseGenericB)), "68");
    Assert.IsTrue(typeof(ChildGenericA4).IsSubClassOfGeneric(typeof(IBaseGenericB)), "69");
    Assert.IsFalse(typeof(ChildGenericA3).IsSubClassOfGeneric(typeof(BaseGenericB)), "68-2");
    Assert.IsTrue(typeof(ChildGenericA3).IsSubClassOfGeneric(typeof(BaseGenericB)), "68-3");
    Assert.IsFalse(typeof(ChildGenericA3).IsSubClassOfGeneric(typeof(BaseGenericB)), "68-4");
    Assert.IsFalse(typeof(ChildGenericA4).IsSubClassOfGeneric(typeof(IBaseGenericB)), "69-2");
    Assert.IsTrue(typeof(ChildGenericA4).IsSubClassOfGeneric(typeof(IBaseGenericB)), "69-3");
    Assert.IsFalse(typeof(ChildGenericA4).IsSubClassOfGeneric(typeof(IBaseGenericB)), "69-4");
    Assert.IsFalse(typeof(bool).IsSubClassOfGeneric(typeof(IBaseGenericB)), "70");
}

用于测试的类和接口:

public class Class1 { }
public class BaseGeneric : IBaseGeneric { }
public class BaseGeneric2 : IBaseGeneric, IInterfaceBidon { }
public interface IBaseGeneric { }
public class ChildGeneric : BaseGeneric { }
public interface IChildGeneric : IBaseGeneric { }
public class ChildGeneric2 : BaseGeneric { }
public interface IChildGeneric2 : IBaseGeneric { }

public class WrongBaseGeneric { }
public interface IWrongBaseGeneric { }

public interface IInterfaceBidon { }

public class ClassA { }
public class ClassB { }
public class ClassC { }
public class ClassB2 : ClassB { }
public class BaseGenericA : IBaseGenericA { }
public class BaseGenericB { }
public interface IBaseGenericB { }
public class BaseGenericA2 : IBaseGenericA, IInterfaceBidonA { }
public interface IBaseGenericA { }
public class ChildGenericA : BaseGenericA { }
public interface IChildGenericA : IBaseGenericA { }
public class ChildGenericA2 : BaseGenericA { }
public class ChildGenericA3 : BaseGenericB { }
public class ChildGenericA4 : IBaseGenericB { }
public interface IChildGenericA2 : IBaseGenericA { }

public class WrongBaseGenericA { }
public interface IWrongBaseGenericA { }

public interface IInterfaceBidonA { }


这是唯一对我有用的解决方案.我找不到任何其他适用于具有多个类型参数的类的解决方案.谢谢.

5> user53564..:

JaredPar的代码可以工作,但只适用于一个级别的继承.对于无限级别的继承,请使用以下代码

public bool IsTypeDerivedFromGenericType(Type typeToCheck, Type genericType)
{
    if (typeToCheck == typeof(object))
    {
        return false;
    }
    else if (typeToCheck == null)
    {
        return false;
    }
    else if (typeToCheck.IsGenericType && typeToCheck.GetGenericTypeDefinition() == genericType)
    {
        return true;
    }
    else
    {
        return IsTypeDerivedFromGenericType(typeToCheck.BaseType, genericType);
    }
}


JaredPar代码中的`while`涵盖了无限级别.

6> 小智..:

这是我创建的一个用于检查对象是否来自特定类型的方法.对我来说很棒!

internal static bool IsDerivativeOf(this Type t, Type typeToCompare)
{
    if (t == null) throw new NullReferenceException();
    if (t.BaseType == null) return false;

    if (t.BaseType == typeToCompare) return true;
    else return t.BaseType.IsDerivativeOf(typeToCompare);
}



7> codybartfast..:

它可能有点矫枉过正,但我​​使用如下的扩展方法.他们检查接口和子类.它还可以返回具有指定泛型定义的类型.

例如,对于问题中的示例,它可以针对通用接口以及泛型类进行测试.返回的类型可用于GetGenericArguments确定泛型参数类型是"SomeType".

/// 
/// Checks whether this type has the specified definition in its ancestry.
///    
public static bool HasGenericDefinition(this Type type, Type definition)
{
    return GetTypeWithGenericDefinition(type, definition) != null;
}

/// 
/// Returns the actual type implementing the specified definition from the
/// ancestry of the type, if available. Else, null.
/// 
public static Type GetTypeWithGenericDefinition(this Type type, Type definition)
{
    if (type == null)
        throw new ArgumentNullException("type");
    if (definition == null)
        throw new ArgumentNullException("definition");
    if (!definition.IsGenericTypeDefinition)
        throw new ArgumentException(
            "The definition needs to be a GenericTypeDefinition", "definition");

    if (definition.IsInterface)
        foreach (var interfaceType in type.GetInterfaces())
            if (interfaceType.IsGenericType
                && interfaceType.GetGenericTypeDefinition() == definition)
                return interfaceType;

    for (Type t = type; t != null; t = t.BaseType)
        if (t.IsGenericType && t.GetGenericTypeDefinition() == definition)
            return t;

    return null;
}

推荐阅读
echo7111436
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有