[Framework 3.5] Risoluzione Overload a runtime

mercoledì 18 novembre 2009 - 14.12

redcloud Profilo | Newbie

Salve a tutti. Mi trovo spesso in una situazione in cui ho un oggetto di tipo base che devo passare come parametro ad un metodo che però accetta un oggetto che eredita dal tipo base. Mi vedo costretto quindi a castare l'oggetto in questione:

Object item = new Pippo(); // mio oggetto

test((Pippo)item);

public void test(Pippo p)
{
...
}

public void test(Pluto p)
{
...
}



Ho visto che con il framework 4.0 è stata introdotta una nuova keyword "dynamic" che mi permetterebbe di scrivere qualcosa del genere:

Object item = new Pippo(); // mio oggetto

testDynamic(item);

public void testDynamic(dynamic p)
{
test(p);
}

public void test(Pippo p)
{
...
}

public void test(Pluto p)
{
...
}

Questo mi eviterebbe di avere un metodo intermedio pieno di clausole "if is" per invocare il giusto metodo in overload. Il problema è che io uso il Framework 3.5. Si può ottenere qualcosa di simile?

aiedail92 Profilo | Expert

Ciao

>Mi trovo spesso in una situazione in cui ho un oggetto
>di tipo base che devo passare come parametro ad un metodo
>che però accetta un oggetto che eredita dal tipo base.

Se hai un tipo base, puoi inserire un metodo virtuale (o astratto) ed eseguirne l'override nelle classi derivate, in questo modo:

class BaseClass { public virtual void Test() { MessageBox.Show("Questo oggetto è stato testato"); } } class Pippo : BaseClass { public override void Test() { MessageBox.Show("Questo oggetto di tipo Pippo è stato testato"); } } class Pluto : BaseClass { public override void Test() { MessageBox.Show("Questo oggetto di tipo Pluto è stato testato"); } }

Quindi per chiamare il metodo Test, ti basta sapere che l'oggetto è un BaseClass (o derivato) e verrà automaticamente chiamato il metodo della classe corretta:

BaseClass item = new Pippo(); item.Test();

In alternativa, puoi creare un'interfaccia (per esempio ITestable) e far implementare l'interfaccia alle classi che supportano quel determinato metodo:

interface ITestable { void Test(); } class Pippo : ITestable { // Implemento l'interfaccia ITestable void ITestable.Test() { MessageBox.Show("Questo oggetto di tipo Pippo è stato testato"); } } class Pluto : ITestable { // Implemento l'interfaccia ITestable void ITestable.Test() { MessageBox.Show("Questo oggetto di tipo Pluto è stato testato"); } }

Quindi per chiamare il metodo corretto, parti dall'interfaccia:

ITestable item = new Pippo(); item.Test();

Luca

redcloud Profilo | Newbie

Grazie per la risposta. In questo modo però sposto la logica dei metodi in overload da una classe esterna alle singole classi e non è il mio caso.

In pratica ho una classe che contiene un metodo in overload con 10 signature diverse. Nella classe che utilizza quest'ultima, come posso evitare di usare una catena di "if is" su un oggetto base (che può essere quindi anche Object) per invocare il giusto metodo in overload in modo dinamico senza utilizzare il framework 4.0 e la parola chiave dynamic?

aiedail92 Profilo | Expert

In tal caso la situazione cambia. Il VB.Net fornisce il cosiddetto "Late Binding" (Associazione tardiva), e dato che C# e VB.Net compilano entrambi codice CIL, è possibile emulare il Late Binding del VB anche in C#

Per farlo devi usare la reflection; ti conviene scrivere una funzione da richiamare per non dover riscrivere il codice ogni volta:

/// <summary> /// Esegue la funzione "FunctionName" con associazione tardiva /// </summary> /// <param name="FunctionContainer"> /// L'oggetto su cui richiamare la funzione, /// oppure null se è una classe statica /// </param> /// <param name="ContainerType"> /// Il tipo dell'oggetto su cui chiamare la funzione. /// Necessario se la classe è statica, altrimenti si può passare null /// </param> /// <param name="FunctionName">Il nome della funzione da chiamare</param> /// <param name="Parameters">I Parametri della funzione</param> /// <returns> /// Il valore di ritorno della funzione, /// o null se non ritorna alcun valore /// </returns> static object LateCall(Object FunctionContainer, Type ContainerType, string FunctionName, [Optional] params Object[] Parameters) { Type[] pTypes = null; if (Parameters != null && Parameters.Length > 0) { pTypes = new Type[Parameters.Length]; for (int i = 0; i < Parameters.Length; i++) { pTypes[i] = Parameters[i].GetType(); } } if (ContainerType == null) { if (FunctionContainer == null) { throw new ArgumentException("Almeno uno fra FunctionContainer e " + "ContainerType deve essere non null"); } ContainerType = FunctionContainer.GetType(); } MethodInfo method = ContainerType.GetMethod(FunctionName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, pTypes, null); if (method != null) { return method.Invoke(FunctionContainer, Parameters); } else { throw new ArgumentException("Funzione in overload non trovata"); } }

A questo punto, se hai una funzione Test all'interno di una classe statica Functions, la chiami in questo modo:

Object item = new Pippo(); LateCall(null, typeof(Functions), "Test", item); item = new Pluto(); LateCall(null, typeof(Functions), "Test", item);

Ti avviso comunque che chiamare una funzione con Late Binding aggiunge molto overhead, e quindi i tempi di chiamata si dilatano parecchio.

Luca
Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2024
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5