marc_s
marc_s

Reputation: 754478

"Downcast" an object to its base type in C#

I'm still working on my general-purpose logging component which should handle any kind of an object and convert it ("serialize" it) to a string.

Works quite well so far - except for one requirement: I should be able to identify the base type of a given object (if one exists), and also list that base type's properties separately.

Basically, I have a method something like this:

public string LogObject(object myObject)

and now I'd like to check to see if this object's type has a base class, and if so - get the properties of my object, "downcast" to its base type.

The checking of the base type isn't hard:

Type baseType = myObject.GetType().BaseType;

but how do I downcast myObject to an object of type baseType now?

I tried several things, like:

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

but that requires my object type to implement IConvertible which I cannot possible require from all my objects...

Any other way?

Those of course don't work....

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

Any other way I'm not thinking of?

Update: I had already thought about doing it by

but that just seems a bit excessive on the reflection - not sure if that'll perform decently....

Upvotes: 5

Views: 4679

Answers (6)

Zoran Horvat
Zoran Horvat

Reputation: 11301

You don't list properties of an object, but properties of the type. This is because properties are read from the type descriptor, rather than the object. You can learn more from this link. Hence, there's no need (and no way in C#; it could be done in C++, though) to cast or convert the object to its base type - it already IS its base type.

On a related note, downcasting means casting from base type to a derived type and that is a tricky operation in that it might fail at run time because it is unknown at compile time whether the object at hand really is of the derived type.

Anyway, solution to your problem is this:

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

On another related note, here is a class library which does exactly what you're trying to do: link

Upvotes: 0

Nicholas Carey
Nicholas Carey

Reputation: 74267

Binding flags are your friend. Given a class hierarchy like this:

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 ; }
}

Code like this:

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" ) ; }

}

produces this:

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

Interesting, though, that properties are exposed both as the property they are and as the methods that implement the property. Wonder how you filter out property methods from the method list?

Upvotes: 0

BenCamps
BenCamps

Reputation: 1844

You could write a Serialize method or better yet override ToString() for all your objects where each object calls the base classes method and appends its own info to what its base class returned.

This might require a bit of extra coding, writing a extra method for each of your classes and doesn't account for classes you didn't write, but if you wish to stay away from reflection it's going to mean you will have to be flexible.

Lastly, why rule out reflection, unless you are going to be doing some ridiculous logging it shouldn't slow you down much and remember "premature optimization is the root of all evil"

Upvotes: 0

marc_s
marc_s

Reputation: 754478

Solution:

If anyone is interested - based on Oded's and Morten's comments, this is the solution I ended up using:

// 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);        
}

Upvotes: 2

Morten Mertner
Morten Mertner

Reputation: 9474

Use BindingFlags.DeclaredOnly to get members declared on a specific type only. Also, even if you do not use this flag, every returned ProperyInfo has a DeclaredType property which lists the type declaring the property.

The only reason I can think of for using DeclaredOnly is if you want better control over the result set, e.g. to filter out virtual properties that have been overridden in subclasses.

PS: Using a library like Fasterflect can make tasks like this a breeze ;)

Upvotes: 4

Ferry van den heuvel
Ferry van den heuvel

Reputation: 227

All properties in your class would also list all of the baseclass ones,

If you want a instance of only the basetype you would have to clone it to a new instance.

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);
     }
}

As long as there is a set method available the value will be copied over from the derived object (called theobejct) to a new obejct called b.

Now this class may also have a base type, and so on. Using generics you could create a single method to deal with this feature for all possible classes.

Do note that if class A has property X and B is derived from A and C from B that you will get the value of property X three times.

Upvotes: -1

Related Questions