“Downcast”一个对象到C#中的基类型

时间:2013-05-14 21:08:37

标签: c# type-conversion

我还在处理我的通用日志记录组件,它应该处理任何类型的object并将其转换(“序列化”它)为字符串。

到目前为止工作得很好 - 除了一个要求:我应该能够识别给定对象的基本类型(如果存在),并且还单独列出该基本类型的属性。

基本上,我有一个像这样的方法:

public string LogObject(object myObject)

现在我想检查一下这个对象的类型是否有基类,如果有的话 - 获取我的对象的属性,“downcast”到它的基类型。

检查基本类型并不难:

Type baseType = myObject.GetType().BaseType;

但是如何将myObject转发给baseType类型的对象?

我尝试了几件事,比如:

object baseObject = Convert.ChangeType(myObject, baseType);

但是这需要我的对象类型来实现IConvertible,这是我无法从所有对象中获得的......

还有其他方式吗?

那些当然不起作用....

object baseObject = myObject as baseType;
object baseObject = (baseType)myObject;

我没想到的任何其他方式?

更新:我已经考虑过这样做了

  • myObject的所有属性抓取到allProperties
  • myObject类型中声明的属性抓取到declaredPropertiesBindingFlag.Declared
  • baseProperties
  • 中减去declaredProperties来获取allProperties

但这在反思中似乎有点过分 - 不确定这是否会表现得体面......

6 个答案:

答案 0 :(得分:4)

使用BindingFlags.DeclaredOnly仅获取在特定类型上声明的成员。此外,即使您不使用此标志,每个返回的ProperyInfo都有一个DeclaredType属性,该属性列出了声明属性的类型。

我能想到使用DeclaredOnly的唯一原因是你想要更好地控制结果集,例如过滤掉已在子类中重写的虚拟属性。

PS:使用像Fasterflect之类的库可以轻松完成这样的任务;)

答案 1 :(得分:2)

<强>解决方案:

如果有人有兴趣 - 基于Oded和Morten的评论,这是我最终使用的解决方案:

// get all the properties of "myObject"
List<PropertyInfo> propertyInfoList = new List<PropertyInfo>(myObject.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public));

// get the object's type and its base type
Type objectType = objectToLog.GetType();
Type baseType = objectToLog.GetType().BaseType;

// if a baseType exists ...
if (baseType != null)
{
    // get the list of properties that are *not* defined directly in "myObject" - 
    // those are all the properties defined in the immediate and possible other base types
    List<PropertyInfo> baseProperties = propertyInfoList.Where(x => x.DeclaringType != objectType).ToList();

    // process those base properties

    // after processing, remove the base properties from the list of "all" properties to get just those
    // properties that are defined directly on the "myObject" type

    List<PropertyInfo> declaredProperties = propertyInfoList.Except(baseProperties);        
}

答案 2 :(得分:0)

您班级中的所有属性也会列出所有基类,

如果您只想要一个基本类型的实例,则必须将其克隆到新实例。

PropertyInfo[] properties = typeof(basetype).GetProperties();
basetype b = new basetype();
foreach (PropertyInfo pi in properties)
{
     if (pi.GetSetMethod() != null)
     {
        pi.SetValue(b, pi.GetValue(theobject,null), null);
     }
}

只要有一个set方法可用,该值就会从派生对象(称为theobjct)复制到一个名为b的新对象。

现在这个类也可能有一个基类型,依此类推。使用泛型,您可以创建一个方法来处理所有可能类的此功能。

请注意,如果A类具有属性X而B来自B中的A和C,那么您将获得属性X的值三次。

答案 3 :(得分:0)

您不会列出对象的属性,而是列出该类型的属性。这是因为属性是从类型描述符而不是对象中读取的。您可以从this链接了解详情。因此,没有必要(并且没有必要在C#中;它可以在C ++中完成)将对象转换或转换为其基类型 - 它已经是它的基类型。

在相关的说明中,向下转换意味着从基类型转换为派生类型,这是一个棘手的操作,因为它可能在运行时失败,因为在编译时未知当前的对象是否真的是派生类型

无论如何,解决问题的方法是:

Type baseType = myObject.GetType().BaseType;
PropertyInfo[] props = null;
if (baseType != null)
    props = baseType.GetProperties();
else
    props = new PropertyInfo[0]; // Just ensure that props is non-null

在另一个相关的说明中,这是一个类库,它完全符合您的要求:link

答案 4 :(得分:0)

你可以编写一个Serialize方法或更好的方法覆盖所有对象的ToString(),其中每个对象调用基类方法并将其自己的信息附加到其基类返回的内容。

这可能需要一些额外的编码,为你的每个课程编写一个额外的方法,并没有考虑你没有写的课程,但如果你想远离反思,那就意味着你将拥有要灵活。

最后,为什么要排除反思,除非你要做一些荒谬的记录,否则它不应该让你失望太多并记住“过早优化是所有邪恶的根源”

答案 5 :(得分:0)

绑定标志是你的朋友。给定这样的类层次结构:

class Foo
{
    public string FooProperty { get ; set ; }
    public virtual void FooMethod1() { return ; }
    public virtual void FooMethod2() { return ; }
}
class Bar : Foo
{
    public string FooProperty { get ; set ; }
    public override void FooMethod1() { return ; }
    public void BarMethod1() { return ; }
}

这样的代码:

for ( Type t = typeof( Bar ) ; t != null ; t = t.BaseType )
{
  MemberInfo[] members = t.GetMembers( BindingFlags.DeclaredOnly|BindingFlags.Instance|BindingFlags.Public ) ;

  Console.WriteLine() ;
  Console.WriteLine( "Type {0}:" , t.FullName ) ;

  // enumerate the methods directly implemented by the type
  Console.WriteLine( "* Methods:" ) ;
  int memberCount = 0 ;
  foreach ( MethodInfo method in members.Where( x => x.MemberType == MemberTypes.Method ).Select( x => (MethodInfo)x ) )
  {
    ++memberCount ;
    Console.WriteLine("  - {0}( {1} )" , method.Name , string.Join(" , " , method.GetParameters().Select(p=>p.ParameterType.Name)) ) ;
  }
  if ( memberCount == 0 ) {  Console.WriteLine("  n/a" ) ; }

  // enumerate the properties directly implemented by the type
  Console.WriteLine( "* Properties:" ) ;
  int propertyCount = 0 ;
  foreach ( PropertyInfo property in members.Where( x => x.MemberType == MemberTypes.Property ).Select( x => (PropertyInfo)x ) )
  {
    ++propertyCount ;
    Console.WriteLine("  - {0}: {1}" , property.Name , property.PropertyType.FullName ) ;
  }
  if ( propertyCount == 0 ) {  Console.WriteLine("  n/a" ) ; }

}

产生这个:

Type ConsoleApplication11.Bar:
* Methods:
  - get_FooProperty(  )
  - set_FooProperty( String )
  - FooMethod1(  )
  - BarMethod1(  )
* Properties:
  - FooProperty: System.String

Type ConsoleApplication11.Foo:
* Methods:
  - get_FooProperty(  )
  - set_FooProperty( String )
  - FooMethod1(  )
  - FooMethod2(  )
* Properties:
  - FooProperty: System.String

Type System.Object:
* Methods:
  - ToString(  )
  - Equals( Object )
  - GetHashCode(  )
  - GetType(  )
* Properties:
  n/a

有趣的是,属性既作为属性公开,也作为实现属性的方法公开。想知道如何从方法列表中过滤出属性方法?