繁体   English   中英

C# - 获取A类中使用的所有类型

[英]C# - Get all types that been used in class A

如何获得特定类型中使用的所有类型?

“MyClass”类的示例:

[MyAttribute(new OtherType(TestEnum.EnumValue1))]
public class MyClass:MyOtherClass
{
    public Type MyType { get; set; }
    public string MyString { get; set; }

    private DateTime MyDateTime;
    [OtherAttribute()]
    public int MyMethod(double doubleNumber, float floatNumber)
    {
        justMyClass myJustClass = new justMyClass();
        return (int)doubleNumber / (int)floatNumber + myJustClass.Count();
    }
}

我想获取类型:MyAttribute,OtherType,TestEnum,MyClass,MyOtherClass,Type,string,DateTime,OtherAttribute,int,double,float和justMyClass。

有什么办法吗?

[MyAttribute(new OtherType(TestEnum.EnumValue1))]

那是无效的,你必须在属性构造函数中有一个常量。 除此之外,如果相当啰嗦,大部分都很容易。

您可以调用typeof(MyClass).CustomAttributes.Select(ca => ca.AttributeType)来获取属性类型, typeof(MyClass).GetFields().Select(fi => fi.FieldType)以获取字段类型, 等等。 Union起来,您将拥有签名和属性中的所有类型。

MyMethod获取justMyClass是比较棘手的(获得doublefloat很容易,当你输入typeof(MyClass).GetMethods().SelectMany(mi => mi.GetParameters()).Select(pa => pa.ParameterType)时,它们会出现typeof(MyClass).GetMethods().SelectMany(mi => mi.GetParameters()).Select(pa => pa.ParameterType)和返回类型的int类似)。

在调试版本中,我们可以期望该方法编译为这样的东西:

.method public hidebysig instance int32 MyMethod (float64 doubleNumber, float32 floatNumber) cil managed 
{
  .custom instance void Temp.Program/OtherAttribute::.ctor() = (
    01 00 00 00
  )
  .maxstack 2
  .locals init (
    [0] class Temp.Program/justMyClass myClass,
    [1] int32 ret
  )

  nop
  newobj instance void Temp.Program/justMyClass::.ctor()
  stloc.0
  ldarg.1
  conv.ovf.i4
  ldarg.2
  conv.ovf.i4
  div
  stloc.1
  ret
}

如果我们完成的话,我们可以简单地获得intjustMyClass

typeof(MyClass)
  .GetMethods()
  .Select(mi => mi.GetMethodBody())
  .Where(mb => mb != null)
  .SelectMany(mb => mb.LocalVariables)
  .Select(lv => lv.LocalType)

但是,对于发布版本,我们希望它可以编译为更像:

.method public hidebysig instance int32 MyMethod (float64 doubleNumber, float32 floatNumber) cil managed 
{
  .custom instance void Temp.Program/OtherAttribute::.ctor() = (
    01 00 00 00
  )
  .maxstack 8

  newobj instance void Temp.Program/justMyClass::.ctor()
  pop
  ldarg.1
  conv.i4
  ldarg.2
  conv.i4
  div
  ret
}

并且因为没有使用调用new justMyClass()的结果,所以它不存储在本地用于调试目的。 即使是使用的值也可能只是从堆栈中的位置使用而不是存储在locals数组中,因此更真实的代码可能会导致类型被遗漏。 相反,你必须实际拆解电话。 这种方法的一个开端是:

private static IEnumerable<Type> GetUsedTypes(Type type, MethodInfo mi)
{
  var body = mi.GetMethodBody();
  if(body == null) // not managed code
    yield break;
  var cil = body.GetILAsByteArray();
  for(int idx = 0; idx < cil.Length; ++idx)
  {
    switch(cil[idx])
    {
      case 0x73: // newobj
        int token = cil[++idx];
        token |= (int)cil[++idx] << 8;
        token |= (int)cil[++idx] << 16;
        token |= (int)cil[++idx] << 24;
        yield return type.Module.ResolveMethod(token).DeclaringType;
        break;
      /* TODO: Other opcodes that deal with types */
    }
  }
}

上面的这个案例只涉及newobj ,通过检查newobj的构造函数,并没有检查泛型类型参数等。该方法必须扩展到也处理callcallvirt等,并确保它没有'看到一个0x73实际上是另一个令牌的一部分,等等。这是相当多的工作,但上面足以表明它可以工作(在这种情况下确实给出了正确的答案)。

尽管如此,有三种情况并不像预期的那样。

在代码中寻找一个; 如果有internalcall你将无法检查任何方法体。

另一个是你可能会对更复杂的案例版本感到惊讶,例如:

public void HasNoType()
{
  if(false)
    throw new Exception("Impossible");
}

虽然源有一个bool ,一个string和一个Exception编译时,死代码将被删除,所以一个调试编译可能有一个bool (所以我们可以看到调试器中的false )和一个发布编译将什么都没有,只是立刻ret

另一个案例是:

public bool CheckIsInRangeWhenAlreadyWeKnowSizeIsNotNegative(int idx, int size)
{
  return (uint)idx < (uint)size;
}

从第一眼看来,这似乎是使用uint ,但事实并非如此。 相反,CIL产生的是:

.method public hidebysig  instance bool CheckIsInRangeWhenAlreadyWeKnowSizeIsNotNegative (int32 idx, int32 size) cil managed
{
  .maxstack 8
  ldarg.1
  ldarg.2
  clt.un
  ret
}

在CIL中,堆栈上的32位整数不完全是Int32UInt32 ,但行为不同取决于对它们执行的操作,因此没有转换,代码中没有使用uint ,但它们只是在比较中一种无符号的方式,C#中最接近的东西是转换为uint然后进行比较。 因此,在检查方法体时,你不会在那里找到一个uint ,除非你进一步进行反汇编并实际反编译,并且看到在C#中表达它的唯一方法是使用uint

因此,虽然获取作为签名和属性一部分的所有类型都很容易(如果相当复杂),获取所有本地使用的类型要么非常困难,要么根本不可能取决于您认为是本地使用的类型。

暂无
暂无

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

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