Argumentos opcionais de saída / ref do C # 4.0


212

O C # 4.0 permite argumentos outou opcionais ref?


2
Bem, C ++ efetivamente os possui para parâmetros "out" - você pode ter um argumento de endereço inicializado como null e é bastante comum escrever código de biblioteca que somente preencherá essa estrutura de retorno se o ponteiro for não nulo. Esse é um idioma que volta ao uso de null para "argumentos opcionais" nas APIs C.
Andy Dent

53
@ Ed e todos: por que isso não faria sentido? Se uma função "retorna" um valor via "out", não quero ser forçado a aceitá-lo. Agora eu sei que, por razões técnicas, o compilador ainda precisa passar algo, mas não há razão para que ele não possa simplesmente criar um local fictício para mim pelas minhas costas.
Roman Starkov

5
Talvez não faça sentido do ponto de vista de como as coisas são implementadas ou o que realmente é um parâmetro opcional. Mas, como os romkyns disseram, seria muito bom ter "argumentos opcionais de saída" - analise isso em inglês, e não no CLR, e se torne razoável e desejável na IMO.
precisa

9
C # não, mas o VB.NET sim.
21410 Jason

5
Isso foi espancado até a morte, no entanto, não posso deixar de mencionar meu apoio a argumentos opcionais. Eu me acostumei bastante a argumentos opcionais por referência, definindo um nullpadrão ( eu venho do PHP ) e testando para nullcontinuar preenchendo o argumento ( para os familiares, pensepreg_match() ) De qualquer forma, embora eu entenda de um ponto técnico, isso pode ser atualmente impossível, e que PHP e C # sejam bastante incomparáveis, ainda assim seria uma ferramenta " agradável " de ter disponível.
21812 Dan Lugg

Respostas:


93

Como já mencionado, isso simplesmente não é permitido e acho que faz muito sentido. No entanto, para adicionar mais alguns detalhes, aqui está uma citação da Especificação do C # 4.0 , seção 21.1:

Parâmetros formais de construtores, métodos, indexadores e tipos de delegados podem ser declarados opcionais:

-parâmetro fixo:
    atributos opt parâmetro modificador opt tipo de identificador default-argumento opt
default-argumento:
    = expressão

  • Um parâmetro fixo com um argumento padrão é um parâmetro opcional , enquanto um parâmetro fixo sem um argumento padrão é um parâmetro obrigatório .
  • Um parâmetro necessário não pode aparecer após um parâmetro opcional em uma lista de parâmetros formal .
  • Um parâmetro refou outnão pode ter um argumento padrão .

Como alternativa, você pode criar uma sobrecarga com um parâmetro ref / out. Sim, você vai ter duas definições de funções, mas vai realizar o que você está depois
Chad

201

Não.

Uma solução alternativa é sobrecarregar com outro método que não possui parâmetros de saída / ref e que apenas chama seu método atual.

public bool SomeMethod(out string input)
{
    ...
}

// new overload
public bool SomeMethod()
{
    string temp;
    return SomeMethod(out temp);
}

Atualização: se você possui o C # 7.0 , pode simplificar:

// new overload
public bool SomeMethod()
{
    return SomeMethod(out _);    // declare out as an inline discard variable
}

(Obrigado @Oskar / @Reiner por apontar isso.)


6
alguma idéia para uma solução mais elegante do que declarar temp / dummy?
Louis Rhys

20
O que não é elegante nisso? Parece perfeitamente decente para mim.
Neutrino

27
Talvez decente, mas elegante esteja definitivamente em outra liga. O fato de não haver uma solução melhor não significa que este seja o estado da arte por decreto.
o0 '.

7
Segure @ o0 '. No C # 7.0, você será capaz de fazer isso: return SomeMethod(out string temp). Veja mais aqui: blogs.msdn.microsoft.com/dotnet/2016/08/24/…
Oskar

7
No C # 7.0, você pode usar uma variável temporária somente para gravação, como:return SomeMethod(out _);
Reiner

64

Não, mas outra ótima alternativa é fazer com que o método use uma classe de modelo genérica para parâmetros opcionais da seguinte maneira:

public class OptionalOut<Type>
{
    public Type Result { get; set; }
}

Então você pode usá-lo da seguinte maneira:

public string foo(string value, OptionalOut<int> outResult = null)
{
    // .. do something

    if (outResult != null) {
        outResult.Result = 100;
    }

    return value;
}

public void bar ()
{
    string str = "bar";

    string result;
    OptionalOut<int> optional = new OptionalOut<int> ();

    // example: call without the optional out parameter
    result = foo (str);
    Console.WriteLine ("Output was {0} with no optional value used", result);

    // example: call it with optional parameter
    result = foo (str, optional);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional.Result);

    // example: call it with named optional parameter
    foo (str, outResult: optional);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional.Result);
}

19
É uma solução bastante razoável, mas uma coisa a ter em atenção é que o compilador não aplicará o requisito de que o parâmetro out seja atribuído antes de sair do método.
Ken Smith

2
Eu gosto, mas se você não quiser criar uma nova classe, pode simulá-la passando em uma única matriz de elementos.
Zumalifeguard

30

Na verdade, existe uma maneira de fazer isso que é permitida pelo C #. Isso volta ao C ++ e viola a boa estrutura Orientada a Objeto do C #.

USE ESTE MÉTODO COM CUIDADO!

Aqui está a maneira como você declara e escreve sua função com um parâmetro opcional:

unsafe public void OptionalOutParameter(int* pOutParam = null)
{
    int lInteger = 5;
    // If the parameter is NULL, the caller doesn't care about this value.
    if (pOutParam != null) 
    { 
        // If it isn't null, the caller has provided the address of an integer.
        *pOutParam = lInteger; // Dereference the pointer and assign the return value.
    }
}

Em seguida, chame a função assim:

unsafe { OptionalOutParameter(); } // does nothing
int MyInteger = 0;
unsafe { OptionalOutParameter(&MyInteger); } // pass in the address of MyInteger.

Para que isso seja compilado, você precisará ativar o código não seguro nas opções do projeto. Esta é uma solução realmente hacky que geralmente não deve ser usada, mas se você tomar uma decisão estranha, misteriosa, misteriosa e inspirada em gerenciamento, REALMENTE precisa de um parâmetro de saída opcional em C #, isso permitirá que você faça exatamente isso.


6

ICYMI: Incluídos nos novos recursos do C # 7.0 enumerados aqui , "descartes" agora são permitidos como parâmetros de saída na forma de _, para permitir que você ignore parâmetros de que não se preocupa:

p.GetCoordinates(out var x, out _); // I only care about x

PS Se você também estiver confuso com a parte "out var x", leia o novo recurso sobre "Out Variables" no link também.


é _ ou * "p.GetCoordinates (fora int x, fora *); // Eu só se preocupam com x"
Onur Topal

parece que eu estava procurando uma versão mais antiga do documento.
Onur Topal

2

Não, mas você pode usar um delegado (por exemplo, Action ) como alternativa.

Inspirado em parte pela resposta de Robin R ao enfrentar uma situação em que pensei que queria um parâmetro opcional de saída, usei um Actiondelegado. Emprestei seu código de exemplo para modificá-lo para uso Action<int>, a fim de mostrar as diferenças e semelhanças:

public string foo(string value, Action<int> outResult = null)
{
    // .. do something

    outResult?.Invoke(100);

    return value;
}

public void bar ()
{
    string str = "bar";

    string result;
    int optional = 0;

    // example: call without the optional out parameter
    result = foo (str);
    Console.WriteLine ("Output was {0} with no optional value used", result);

    // example: call it with optional parameter
    result = foo (str, x => optional = x);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional);

    // example: call it with named optional parameter
    foo (str, outResult: x => optional = x);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional);
}

Isso tem a vantagem de a variável opcional aparecer na fonte como um int normal (o compilador a agrupa em uma classe de fechamento, em vez de agrupá-la explicitamente em uma classe definida pelo usuário).

A variável precisa de inicialização explícita porque o compilador não pode assumir que o Action chamada será antes da saída da chamada da função.

Não é adequado para todos os casos de uso, mas funcionou bem para o meu caso de uso real (uma função que fornece dados para um teste de unidade e onde um novo teste de unidade precisava de acesso a algum estado interno não presente no valor de retorno).


0

Use um método sobrecarregado sem o parâmetro out para chamar aquele com o parâmetro out para C # 6.0 e inferior. Não sei por que um C # 7.0 para .NET Core é a resposta correta para esse segmento quando foi perguntado especificamente se o C # 4.0 pode ter um parâmetro opcional de saída. A resposta é não!


-2

Que tal assim?

public bool OptionalOutParamMethod([Optional] ref string pOutParam)
{
    return true;
}

Você ainda precisa passar um valor para o parâmetro em C #, mas é um parâmetro opcional ref.


8
“Você ainda precisa passar um valor para o parâmetro em C #” ... Isso o torna não opcional.
Arturo Torres Sánchez

C # ignora a [Optional]anotação. Isso não ajuda.
Home

-4
void foo(ref int? n)
{
    return null;
}

1
Por favor, adicione um pouco de explicação sobre o código para fazer com que todos entendem o conceito facilmente
techspider

1
Embora esse código possa responder à pergunta, fornecer um contexto adicional sobre por que e / ou como responde à pergunta melhoraria significativamente seu valor a longo prazo. Por favor edite sua resposta para adicionar alguma explicação.
Toby Speight

2
Isso resultará em um erro de sintaxe, pois o método tem um tipo de retorno nulo. Além disso, não responde a pergunta.
Nathan Montez 12/01
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.