2015-06-26 21 views
5

ho funzione in C++ o meno così:Come convertire argomento puntatore opzionale da codice C++ a C#

// C++ 
bool Foo(int* retVal = NULL) 
{ 
    // ... 
    if (retVal != NULL) 
     *retVal = 5; 
    // ... 
    return true; 
} 

E posso utilizzare le funzioni in due modi:

int ret; 
Foo(&ret); 

Foo(); 

Quando scrivo il codice in C# ho usato parola chiave ref:

// C# 
bool Foo(ref int retVal = null) 
{ 
    // ... 
    if (retVal != null) 
    { 
     retVal = 5; 
    } 
    // ... 
    return true; 
} 

ma compilatore detto:

Un parametro ref o out non può avere un valore predefinito.

Come posso risolvere questo problema?

+3

È possibile utilizzare un tipo nullable come 'int?'. Vedi: https://msdn.microsoft.com/en-us/library/2cf62fcy.aspx – NathanOliver

+0

@NathanOliver Si noti che mentre questo funzionerà con 'ref int', per parametri come' ref string' questo non funziona – xanatos

+0

@ xanatos non sono stringhe annullabili di default in C# quindi non dovrai usare '?'? – NathanOliver

risposta

3

Il più semplice modo sarebbe quella di scrivere solo un sovraccarico:

bool Foo() 
{ 
    int notUsed; 
    return Foo(ref notUsed); 
} 

bool Foo(ref int retVal) 
{ 
    // ... 
    retVal = 5; 
    // ... 
    return true; 
} 

Se effettivamente bisogno di sapere se è necessario il valore ref, beh, è ​​comunque possibile utilizzare i puntatori, ma avrete bisogno di un contesto unsafe:

unsafe bool Foo() 
{ 
    return Foo(null); 
} 

unsafe bool Foo(ref int retVal) 
{ 
    return Foo(&retVal); 
} 

private unsafe bool Foo(int* retVal) 
{ 
    // ... 
    if (retVal != null) 
    { 
     *retVal = 5; 
    } 
    // ... 
    return true; 
} 

Oppure, senza unsafe come suggerito nei commenti, come puntatori in C# possono essere considerati come artiglieria pesante:

bool Foo() 
{ 
    var notNeeded = 0; 
    return Foo(ref notNeeded, false); 
} 

bool Foo(ref int retVal) 
{ 
    return Foo(ref retVal, true); 
} 

private bool Foo(ref int retVal, bool retValNeeded) 
{ 
    // ... 
    if (retValNeeded) 
    { 
     retVal = 5; 
    } 
    // ... 
    return true; 
} 
+0

Stavo pensando la stessa cosa (la mia prima revisione era la stessa http://stackoverflow.com/revisions/31075073/1). Ma poi ho notato che stiamo perdendo informazioni qui ... La versione C può distinguere se "retVal *" è stato passato o meno, e forse fare qualche calcolo pesante per restituirlo, mentre il C# non può farlo ... La versione C# è zucchero puramente sintattico per rendere il metodo più facile da chiamare. – xanatos

+0

@xanatos si, ho notato che 'if' nel codice originale ... scriverò un secondo modo. –

+1

Codice non sicuro ... Per favore non farlo ... Pensa ai bambini!:-) Se vuoi davvero, potresti passare un array di un elemento ... O meglio semplicemente un parametro 'bool' aggiuntivo. Stai già schermando il metodo "reale" con due metodi ... Aggiungi al metodo reale un "bool". – xanatos

3

Il ref di C# è più simile allo & (riferimento) di C++ rispetto allo * di C e C++ (puntatore). Come riferimenti C++, devono fare riferimento a qualcosa.

Ora, si potrebbe:

public class OptionalRef<T> 
{ 
    public T Value { get; set; } 

    public static implicit operator OptionalRef<T>(T value) 
    { 
     return new OptionalRef<T> { Value = value }; 
    } 

    public static implicit operator T(OptionalRef<T> optional) 
    { 
     return optional.Value; 
    } 

    public override string ToString() 
    { 
     return Value != null ? Value.ToString() : null; 
    } 
} 

poi

static bool Foo(OptionalRef<int> retVal = null) 
{ 
    // ... 
    if (retVal != null) 
    { 
     retVal.Value = 5; 
    } 
    // ... 
    return true; 
} 

e lo si utilizza come:

Foo(); // null passed 
Foo(null); // same 

Foo(5); // not interested if the value is changed 

// Full use 
OptionalRef<int> val = 5; 
Foo(val); 
int ret = val; 

nota che non sto approvando pienamente la semantica che ho scritto

E 'più di un caso di abbiamo chiesto qualcosa, ti ho dato qualcosa, senza fare domande

+0

Mi piace il tuo disclaimer. Puoi approfondire ciò che pensi di questo un po 'di più? Non devi metterlo nella tua risposta, ma c'è un modo migliore per farlo, o ti piace la soluzione ref? – thinklarge

+0

@think Supporto pienamente i pattern di Lucas, escludendo quello non sicuro. MA Sento che sono tutti più o meno hack. Mi piace un po 'di più il mio, ma stiamo ancora parlando di scegliere un vegetale anziché un altro. Non c'è pesce o carne da vedere a questo tavolo. – xanatos

+0

@think è importante comprendere che non tutto ciò che può essere fatto facilmente in C può essere fatto facilmente in C#. I buoni modelli C non sono sempre buoni modelli C#. Quindi dobbiamo capire, accettare e portare avanti, e non cercare di emulare ogni angolo di C – xanatos

2

mi piacerebbe andare su questo uno dei due modi. Se si desidera VERAMENTE avere un riferimento al proprio oggetto, è possibile includerlo in una classe. Questi vengono sempre passati per riferimento in modo da ottenere la modifica nel modo desiderato e puoi istanziarla a null.

Ecco un esempio.

public class HolderObject 
{ 
    public string myStr {get; set;} 
} 
public class Program 
{ 

    public static void Main() 
    {  
     var xyz = new HolderObject() { 
      myStr = "1234" 
     }; 
     Console.WriteLine(xyz.myStr); 
     FixString(xyz); 
     Console.WriteLine(xyz.myStr); 
     FixString(); 
     Console.WriteLine(xyz.myStr); 


    } 

    private static bool FixString(HolderObject input = null) 
    { 
     if (input != null) 
      input.myStr = "test"; 
     return true; 
    } 
} 

stampe

1234 
test 

Un'altra soluzione è quella di sovraccaricare la vostra funzione.

bool Foo() 
{   
    // ... 
    return true; 
} 

bool Foo(ref int retVal = null) 
{ 
    // ... 
    if (retVal != null) 
    { 
     retVal = 5; 
    } 
    return Foo(); 
} 

Davvero non mi piace. In realtà sto lavorando al codice in C# che è stato estratto direttamente da C++. Funzioni nidificate a 6 o 7 livelli che modificano in profondità un oggetto passato per riferimento. È difficile da leggere e se si osservano le avvertenze sull'analisi del codice, suggerirà di non utilizzare valori di riferimento.

Se è possibile, vorrei allontanarmi dal passare da ref il più possibile e restituire il valore che è stato modificato. Oppure passa un oggetto che contiene sia il tuo valore bool che il tuo nuovo valore.

Problemi correlati