In che modo la riflessione mi dice quando una proprietà nasconde un membro ereditato con la "nuova" parola chiave?

StackOverflow https://stackoverflow.com/questions/288357

Domanda

Quindi se ho:

public class ChildClass : BaseClass
{
    public new virtual string TempProperty { get; set; }
}

public class BaseClass
{
    public virtual string TempProperty { get; set; }
}

Come posso usare la riflessione per vedere che ChildClass nasconde l'implementazione Base di TempProperty?

Vorrei che la risposta fosse agnostica tra c # e vb.net

È stato utile?

Soluzione

Dovremo trattare in termini di metodi della proprietà qui piuttosto che della proprietà stessa, perché sono i metodi get / set della proprietà che vengono effettivamente sovrascritti piuttosto che la proprietà stessa. Userò il metodo get poiché non dovresti mai avere una proprietà senza una, anche se una soluzione completa dovrebbe verificare la mancanza di una.

Guardando l'IL emesso in un numero di casi, il metodo 'get' della proprietà base avrà i token dei metadati (questo proviene dal compilatore C #; altri potrebbero non emettere il hidebysig a seconda sul loro metodo per nascondere la semantica, nel qual caso il metodo sarebbe nascosto per nome):

non-virtual : .method public hidebysig specialname instance
virtual     : .method public hidebysig specialname newslot virtual instance 

Quello derivato avrà i seguenti token:

override    : .method public hidebysig specialname virtual instance 
new         : .method public hidebysig specialname instance
new virtual : .method public hidebysig specialname newslot virtual instance 

Quindi possiamo vedere da questo che non è possibile dire dai token dei metadati del metodo se è nuovo perché il metodo di base non virtuale ha gli stessi token del non virtuale nuovo e il metodo di base virtuale ha gli stessi token del nuovo metodo virtuale .

Ciò che possiamo è che se il metodo ha il token virtual ma non il token newslot , sostituisce un metodo di base anziché lo ombreggia, cioè

var prop = typeof(ChildClass).GetProperty("TempProperty");
var getMethod = prop.GetGetMethod();
if ((getMethod.Attributes & MethodAttributes.Virtual) != 0 &&
    (getMethod.Attributes & MethodAttributes.NewSlot) == 0)
{
    // the property's 'get' method is an override
}

Supponendo quindi che il metodo 'get' non sia una sostituzione, vogliamo sapere se esiste una proprietà nella classe base che sta oscurando. Il problema è che, poiché il metodo si trova in uno slot della tabella dei metodi diverso, in realtà non ha alcuna relazione diretta con il metodo in uso. Quindi quello che stiamo effettivamente dicendo è "il tipo di base ha un metodo che soddisfa i criteri per l'ombreggiamento", che varia a seconda che il metodo sia hidebysig o hide-by-name.

Per il primo dobbiamo verificare se la classe base ha un metodo che corrisponde esattamente alla firma, mentre per il secondo dobbiamo verificare se ha un metodo con lo stesso nome, quindi continuando il codice dall'alto:

else 
{
    if (getMethod.IsHideBySig)
    {
        var flags = getMethod.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic;
        flags |= getMethod.IsStatic ? BindingFlags.Static : BindingFlags.Instance;
        var paramTypes = getMethod.GetParameters().Select(p => p.ParameterType).ToArray();
        if (getMethod.DeclaringType.BaseType.GetMethod(getMethod.Name, flags, null, paramTypes, null) != null)
        {
            // the property's 'get' method shadows by signature
        }
    }
    else
    {
        var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
        if (getMethod.DeclaringType.BaseType.GetMethods(flags).Any(m => m.Name == getMethod.Name))
        {
            // the property's 'get' method shadows by name
        }
    }
}

Penso che questo sia quasi tutto lì, ma ancora non penso che sia esattamente giusto. Per cominciare, non ho alcuna familiarità con il nascondere in base al nome poiché C # non lo supporta e questo è praticamente tutto ciò che uso, quindi potrei sbagliarmi nel codice qui che indica che un metodo di istanza potrebbe oscurare uno statico. Inoltre, non conosco il problema della distinzione tra maiuscole e minuscole (ad esempio, in VB un metodo chiamato Foo potrebbe essere un metodo chiamato foo se entrambi avessero la stessa firma e fossero entrambi < code> hidebysig - in C # la risposta è no ma se la risposta è sì in VB significa che la risposta a questa domanda è in realtà non deterministica).

Beh, non sono sicuro di quanto aiuto sia tutto questo, oltre a illustrare che in realtà è un problema molto più difficile di quanto pensassi sarebbe (o mi sono perso qualcosa di veramente ovvio nel qual caso mi piacerebbe sapere!). Ma spero che il contenuto sia sufficiente per aiutarti a ottenere quello che stai cercando di fare.

Altri suggerimenti

Non sembra che il riflesso ti dia questo di default, quindi dovrai farlo tu:

public static bool IsHidingMember( this PropertyInfo self )
{
    Type baseType = self.DeclaringType.BaseType;
    PropertyInfo baseProperty = baseType.GetProperty( self.Name, self.PropertyType );

    if ( baseProperty == null )
    {
        return false;
    }

    if ( baseProperty.DeclaringType == self.DeclaringType )
    {
        return false;
    }

    var baseMethodDefinition = baseProperty.GetGetMethod().GetBaseDefinition();
    var thisMethodDefinition = self.GetGetMethod().GetBaseDefinition();

    return baseMethodDefinition.DeclaringType != thisMethodDefinition.DeclaringType;
}

Non sono sicuro di come funzionerà con le proprietà indicizzate!

Non ho mai fatto quello che stai cercando di fare, ma il metodo MethodInfo.GetBaseDefinition () sembra essere quello che stai cercando.

Restituisce il MethodInfo questo metodo ha la precedenza.

Da MSDN:

  

Se viene specificato un determinato metodo con la nuova parola chiave (come nel newslot come descritto in Membri di tipo), viene restituito il metodo indicato.

Correzione, se stai usando VB la proprietà che stai cercando è " IsHideBySig " ;. Ciò sarà falso nel caso in cui il "nuovo" la parola chiave è stata utilizzata per definire un metodo / proprietà.

Nel caso C #, entrambe le istanze vengono emesse come " hidebysig " ;. Grazie per averlo sottolineato Greg. Non mi ero reso conto di averlo testato solo in VB. Ecco un codice VB di esempio che riproporrà questo comportamento.

Module Module1

    Class Foo
        Public Function SomeFunc() As Integer
            Return 42
        End Function
    End Class

    Class Bar
        Inherits Foo
        Public Shadows Function SomeFunc() As Integer
            Return 36
        End Function
    End Class

    Sub Main()
        Dim type = GetType(Bar)
        Dim func = type.GetMethod("SomeFunc")
        Stop
    End Sub

End Module
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top