Costumo me escrever funções que se parecem com isso, porque elas permitem que eu zombe facilmente do acesso a dados e ainda forneço uma assinatura que aceita parâmetros para determinar quais dados acessar.
public static string GetFormattedRate(
Func<string, RateType>> getRate,
string rateKey)
{
var rate = getRate(rateKey);
var formattedRate = rate.DollarsPerMonth.ToString("C0");
return formattedRate;
}
Ou
public static string GetFormattedRate(
Func<RateType, string> formatRate,
Func<string, RateType>> getRate,
string rateKey)
{
var rate = getRate(rateKey);
var formattedRate = formatRate(rate);
return formattedRate;
}
Então eu uso algo assim:
using FormatterModule;
public static Main()
{
var getRate = GetRateFunc(connectionStr);
var formattedRate = GetFormattedRate(getRate, rateType);
// or alternatively
var formattedRate = GetFormattedRate(getRate, FormatterModule.FormatRate, rateKey);
System.PrintLn(formattedRate);
}
Isto é uma prática comum? Eu sinto que deveria estar fazendo algo mais como
public static string GetFormattedRate(
Func<RateType> getRate())
{
var rate = getRate();
return rate.DollarsPerMonth.ToString("C0");
}
Mas isso não parece funcionar muito bem porque eu teria que criar uma nova função para passar para o método para cada tipo de taxa.
Às vezes eu sinto que deveria estar fazendo
public static string GetFormattedRate(RateType rate)
{
return rate.DollarsPerMonth.ToString("C0");
}
Mas isso parece remover qualquer busca e formato reutilizáveis. Sempre que quero buscar e formatar, tenho que escrever duas linhas, uma para buscar e outra para formatar.
O que sinto falta na programação funcional? É o caminho certo para fazê-lo ou existe um padrão melhor, fácil de manter e usar?
GetFormattedRate()
aceitar a taxa para formatar como parâmetro, em vez de aceitar uma função que retorna a taxa para formatar como parâmetro?closures
onde você passa o próprio parâmetro para uma função, o que, por sua vez, fornece uma função referente a esse parâmetro específico. Essa função "configurada" seria passada como parâmetro para a função que a utiliza.Respostas:
Se você fizer isso por tempo suficiente, acabará escrevendo esta função repetidamente:
Parabéns, você inventou a composição das funções .
Funções de wrapper como essa não têm muita utilidade quando são especializadas em um tipo. No entanto, se você introduzir algumas variáveis de tipo e omitir o parâmetro de entrada, sua definição de GetFormattedRate será semelhante a esta:
Tal como está, o que você está fazendo tem pouco propósito. Como não é genérico, é necessário duplicar esse código em todo o lugar. Isso complica demais o seu código, porque agora ele precisa reunir tudo o que precisa de milhares de pequenas funções por conta própria. Porém, seu coração está no lugar certo: você só precisa se acostumar a usar esses tipos de funções genéricas de ordem superior para organizar as coisas. Ou, use um bom lambda moda antiga para ligar
Func<A, B>
eA
paraFunc<B>
.Não se repita.
fonte
FormatRate(GetRate(rateKey))
.GetFormattedRate
diretamente a partir de agora.Func<Func<A, B>, C>
); isso significa que você só precisa de uma função de composição que funcione para qualquer função. No entanto, você pode trabalhar com funções C # bem o suficiente apenas usando fechamentos - em vez de passarFunc<rateKey, rateType>
, você só precisa realmenteFunc<rateType>
e, ao passar a função, constrói-a assim() => GetRate(rateKey)
. O ponto é que você não expõe os argumentos com os quais a função de destino não se importa.Compose
função é realmente útil apenas se você precisar atrasar a execuçãoGetRate
por algum motivo, como se você deseja passarCompose(FormatRate, GetRate)
para uma função que fornece uma taxa de sua própria escolha, por exemplo, aplicá-la a todos os elementos em um Lista.Não há absolutamente nenhuma razão para passar uma função e seus parâmetros, apenas para chamá-la com esses parâmetros. De fato, em seu caso, você não tem nenhuma razão para passar uma função em tudo . O chamador pode também chamar a própria função e passar o resultado.
Pense nisso - em vez de usar:
por que não simplesmente usar:
?
Além de reduzir o código desnecessário, também reduz o acoplamento - se você deseja alterar a forma como a taxa é obtida (por exemplo, se
getRate
agora precisa de dois argumentos), não é necessário alterarGetFormattedRate
.Da mesma forma, não há razão para escrever em
GetFormattedRate(formatRate, getRate, rateKey)
vez de escreverformatRate(getRate(rateKey))
.Não complique demais as coisas.
fonte
formatRate
que deve ser mapeado sobre as taxas que devem ser formatadas).Se você absolutamente precisar passar uma função para a função porque ela passa algum argumento extra ou a chama em um loop, você pode passar um lambda:
O lambda vincula os argumentos que a função não conhece e oculta que eles existem.
fonte
Não é isso que você quer?
E então chame assim:
Se você deseja um método que possa se comportar de várias maneiras diferentes em uma linguagem orientada a objetos, como C #, a maneira usual de fazer isso é fazer com que o método chame um método abstrato. Se você não tiver um motivo específico para fazê-lo de maneira diferente, faça-o dessa maneira.
Isso parece uma boa solução, ou há algumas desvantagens em que você está pensando?
fonte
GetFormattedRate
método e apenas chamarIRateFormatter.FormatRate(rate)
). O conceito básico, no entanto, está correto, e acho que o OP também deve implementar seu código polimorficamente, se ele precisar de vários métodos de formatação.