Classe di wrapper generica possibile?
-
03-07-2019 - |
Domanda
Su C # 3.0 e .NET 3.5, immagina che ci sia un'interfaccia:
public interface INameable
{
string Name {get;}
}
e molte classi immutabili che implementano l'interfaccia.
Vorrei avere un unico metodo di estensione
public static T Rename<T>(this T obj) where T : INameable
{
...
}
che restituisce un'istanza di wrapping dell'oggetto originale con solo il nome modificato e tutte le altre proprietà leggono e le chiamate di metodo vengono instradate all'oggetto originale.
Come ottenere una classe wrapper generica per questo, senza implementarla per tutti i tipi di implementazione INameable? Pensi che sia possibile?
Soluzione
No, questo non è possibile, a meno che T
non sia vincolato a essere un'interfaccia o a essere una classe con tutti i membri virtuali, e questo vincolo non è possibile specificare al momento della compilazione (sebbene potresti scrivere un controllo di runtime se sei soddisfatto di questo approccio).
Il motivo per cui non è possibile farlo per tipi arbitrari è che se si prende un oggetto di tipo T
, è necessario restituire un oggetto assegnabile a T
. Se passo un oggetto che appare come segue ...
public sealed class SomeClass : INameable
{
public string Name { get; }
public string Abc { get; }
}
... quindi non è possibile creare un altro tipo assegnabile a SomeClass
. Non usando Reflection.Emit
o qualsiasi altro metodo, perché il tipo è sigillato.
Tuttavia, se sei soddisfatto delle restrizioni che ho citato, puoi usare qualcosa come il framework Castle DynamicProxy per creare un tipo che inoltra l'oggetto passato e intercetta le chiamate per inoltrarle o reimplementarle come . appropriato
Altri suggerimenti
Beh, sì, è possibile, ma implica la generazione di codice in memoria.
Puoi guardare Reflection.Emit e qui .
Nota che coinvolgerà molto codice.
Cioè, se presumo che ti capisca correttamente.
Ecco cosa penso che tu stia chiedendo:
SomeNameableObject a1 = new SomeNameableObject("ThisIsTheFirstName");
SomeNameableObject a2 = a1.Rename("ThisIsTheSecondName");
// a1 works, still has the name ThisIsTheFirstName
// a2 works, but everything is routed to a1,
// except for the name, which is ThisIsTheSecondName
È corretto?