Pergunta

Esta é apenas uma curiosidade pergunta que eu estava me perguntando se alguém tinha uma boa resposta para:

No .NET Framework Class Library temos, por exemplo desses dois métodos:

public static IQueryable<TSource> Where<TSource>(
    this IQueryable<TSource> source,
    Expression<Func<TSource, bool>> predicate
)

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate
)

Por que usar Func<TSource, bool> em vez de Predicate<TSource>?Parece que o Predicate<TSource> é utilizado apenas por List<T> e Array<T>, enquanto Func<TSource, bool> é usado por praticamente todos os Queryable e Enumerable métodos e métodos de extensão...por quê?

Foi útil?

Solução

Enquanto Predicate foi introduzido ao mesmo tempo que List<T> e Array<T>, em .net 2.0, as diferentes Func e Action variantes vêm .net 3.5.

Para aqueles Func predicados são usados principalmente para a consistência na operadores LINQ.Como de .net 3.5, sobre a utilização Func<T> e Action<T> o orientação estados:

Não usar os novos tipos de LINQ Func<> e Expression<> em vez de personalizado delegados e predicados

Outras dicas

Eu já me perguntei isso antes. eu gosto de Predicate<T> Delegado - é bom e descritivo. No entanto, você precisa considerar as sobrecargas de Where:

Where<T>(IEnumerable<T>, Func<T, bool>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)

Isso permite filtrar com base no índice da entrada também. Isso é bom e consistente, enquanto:

Where<T>(IEnumerable<T>, Predicate<T>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)

não seria.

Certamente a real razão para o uso de Func em vez de um determinado delegado é que o C# trata separadamente declarado delegados como tipos totalmente diferentes.

Apesar de Func<int, bool> e Predicate<int> ambos têm idênticos argumentos e tipos de retorno, eles não são de atribuição compatível.Então, se cada biblioteca declarado o seu próprio tipo de delegado para cada delegado padrão, as bibliotecas não seria capaz de interoperar, a menos que o usuário insere "ponte" delegados para realizar conversões.

    // declare two delegate types, completely identical but different names:
    public delegate void ExceptionHandler1(Exception x);
    public delegate void ExceptionHandler2(Exception x);

    // a method that is compatible with either of them:
    public static void MyExceptionHandler(Exception x)
    {
        Console.WriteLine(x.Message);
    }

    static void Main(string[] args)
    {
        // can assign any method having the right pattern
        ExceptionHandler1 x1 = MyExceptionHandler; 

        // and yet cannot assign a delegate with identical declaration!
        ExceptionHandler2 x2 = x1; // error at compile time
    }

Por incentivando as pessoas a usar Func, a Microsoft espera que isso irá aliviar o problema de incompatibilidade tipos de delegado.Todos os delegados vão jogar bem juntos, porque eles só vão ser correspondido, de acordo com o seu parâmetro/tipos de retorno.

Não resolve todos os problemas, porque Func (e Action) não pode ter out ou ref parâmetros, mas aqueles que são usados com menos freqüência.

Atualização: nos comentários Svish diz:

Ainda, mudar de um tipo de parâmetro de Func ao Predicado e de volta, não parece fazer qualquer diferença?Pelo menos ele ainda compila sem problemas.

Sim, desde que o seu programa só atribui métodos para delegados, na primeira linha do meu Main função.O compilador silenciosamente gera código para o novo delegado objeto que encaminha para o método.Então, na minha Main a função, que eu poderia mudar x1 para ser do tipo ExceptionHandler2 sem causar um problema.

No entanto, na segunda linha eu tentar atribuir o primeiro delegado a outro delegado.Até pensei que o 2º tipo de delegado tem exatamente os mesmos parâmetros e tipos de retorno, o compilador gera um erro CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'.

Talvez isso irá torná-lo mais claro:

public static bool IsNegative(int x)
{
    return x < 0;
}

static void Main(string[] args)
{
    Predicate<int> p = IsNegative;
    Func<int, bool> f = IsNegative;

    p = f; // Not allowed
}

O meu método IsNegative é uma ótima coisa para atribuir ao p e f variáveis, como eu fazê-lo diretamente.Mas, então, eu não posso atribuir uma dessas variáveis para o outro.

O conselho (em 3,5 ou acima) é usar o Action<...> e Func<...> - Para o "Por quê?" - Uma vantagem é que "Predicate<T>"Só é significativo se você souber o que significa" predicado " - caso contrário, você precisará olhar para o navegador de objetos (etc) para encontrar o Signatute.

Por outro lado Func<T,bool> segue um padrão padrão; Eu posso dizer imediatamente que esta é uma função que leva um T e retorna a bool - Não precisa entender nenhuma terminologia - basta aplicar meu teste de verdade.

Para "predicado", isso pode ter sido bom, mas eu aprecio a tentativa de padronizar. Também permite muita paridade com os métodos relacionados nessa área.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top