2013-05-14 17 views
5

Sto ancora lavorando al mio componente di registrazione generico che dovrebbe gestire qualsiasi tipo di object e convertirlo ("serializzarlo") in una stringa."Downcast" un oggetto al suo tipo base in C#

Funziona abbastanza bene finora, tranne per un requisito: dovrei essere in grado di identificare il tipo di base di un dato oggetto (se ne esiste uno) e anche elencare le proprietà del tipo di base separatamente.

Fondamentalmente, ho un metodo simile a questa:

public string LogObject(object myObject) 

e ora mi piacerebbe verificare se il tipo di questo oggetto ha una classe di base, e in tal caso - ottenere le proprietà di mio oggetto , "downcast" al suo tipo di base.

La verifica del tipo di base non è difficile:

Type baseType = myObject.GetType().BaseType; 

Ma come faccio a Downcast myObject a un oggetto di tipo baseType ora?

ho provato diverse cose, come:

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

ma che richiede il mio tipo di oggetto per implementare IConvertible cui non possibile posso esigere da tutti i miei oggetti ...

Qualsiasi altro modo?

Quelli ovviamente non funzionano ....

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

Qualsiasi altro modo non sto pensando di?

Aggiornamento: avevo già pensato di farlo a

  • afferrare tutte le proprietà di myObject in allProperties
  • afferrando solo quelle proprietà dichiarate dal tipo myObject in declaredProperties (BindingFlag.Declared)
  • ottenendo il baseProperties sottraendo lo declaredProperties da allProperties

ma che sembra solo un po 'eccessivo sulla riflessione - non so se questo esibiranno decentemente ....

+0

Non si possono semplicemente ottenere le proprietà del tipo di base da 'baseType' ed escluderle dall'elenco delle proprietà prese da' myObject'? (probabilmente mostrando la mia ignoranza riguardo alla riflessione) – Oded

+0

Cosa stai cercando di ottenere? 'myObject' è già di tipo' baseType'. – Igor

+0

@Igor: Devo essere in grado di ottenere un elenco delle proprietà di 'baseType' ** solo ** –

risposta

4

Utilizzare BindingFlags.Declared Solo per ottenere membri dichiarati solo su un tipo specifico. Inoltre, anche se non si utilizza questo flag, ogni ProperyInfo restituito ha una proprietà DeclaredType che elenca il tipo che dichiara la proprietà.

L'unica ragione per cui posso pensare di utilizzare DeclaredOnly è se si desidera un controllo migliore sul set di risultati, ad es. filtrare le proprietà virtuali che sono state sovrascritte in sottoclassi.

PS: Usando una libreria come Fasterflect può fare compiti come questo un gioco da ragazzi;)

+0

Grazie, Morten! –

0

Tutte le strutture: la classe sarebbe anche elencare tutti quelli BaseClass,

Se vuoi un'istanza di solo il tipo di base dovresti clonarlo in una nuova istanza.

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

Finché esiste un metodo set disponibile il valore sarà copiato dall'oggetto derivato (chiamato theobejct) in un obejct chiamato b.

Ora questa classe può anche avere un tipo di base e così via. Usando i generici è possibile creare un unico metodo per gestire questa funzionalità per tutte le classi possibili.

Si noti che se la classe A ha le proprietà X e B derivate da A e C da B, si otterrà il valore della proprietà X tre volte.

+1

Ciò presuppone che il tipo di base abbia un costruttore predefinito, e sarà molto più lento del necessario. –

2

Soluzione:

Se qualcuno è interessato - in base a Oded e di commenti di Morten, questa è la soluzione ho finito utilizzando:

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

Semplice dopotutto;) –

0

Non si elencano le proprietà di un oggetto, ma le proprietà del tipo. Questo perché le proprietà vengono lette dal descrittore del tipo, piuttosto che dall'oggetto. Puoi saperne di più dal link this. Quindi, non è necessario (e non c'è modo in C#, potrebbe essere fatto in C++, però) per trasmettere o convertire l'oggetto nel suo tipo di base - è già il suo tipo di base.

Su una nota correlata, downcasting significa eseguire il cast dal tipo di base a un tipo derivato e questa è un'operazione complessa in quanto potrebbe non riuscire in fase di esecuzione perché è sconosciuto al momento della compilazione se l'oggetto in questione è realmente derivato genere.

In ogni caso, soluzione al vostro problema è questo:

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 

In un'altra nota correlata, qui è una libreria di classi, che fa esattamente quello che stai cercando di fare: link

0

si potrebbe scrivere un Metodo Serialize o ancora meglio Override di ToString() per tutti gli oggetti in cui ogni oggetto chiama il metodo delle classi di base e aggiunge le proprie informazioni a ciò che ha restituito la classe base.

Questo potrebbe richiedere un po 'di codifica aggiuntiva, scrivere un metodo extra per ciascuna delle classi e non tiene conto delle classi che non hai scritto, ma se desideri stare lontano dal riflesso significa che lo farai deve essere flessibile

Infine, perché esclude la riflessione, se non avete intenzione di fare un po 'di logging ridicolo non dovrebbe rallentare molto e ricordare "ottimizzazione prematura è la radice di tutti i mali"

0

bandiere di rilegatura sono tuo amico . Data una gerarchia di classe come questa:

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

codice come questo:

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

} 

produce questo:

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 

interessante, però, che le proprietà sono esposti sia come proprietà sono e come i metodi che implementano la proprietà. Ti chiedi come filtri i metodi di proprietà dall'elenco dei metodi?

Problemi correlati