Expression\n\nImagine: você está escrevendo código C#. Precisa passar uma condição de filtro para um método. O que escolher: Func<User, bool> ou Expression<Func<User, bool>>? Aparentemente a diferença é mínima — ambos recebem um usuário e retornam bool. Mas nos bastidores, esconde-se um universo inteiro de diferenças.
\n\nNo Stack Overflow, essa pergunta acumulou milhares de visualizações — e não é à toa. Hoje vamos entender por que Expression<Func<T>> pode ser mais poderoso, mais rápido e mais seguro que o Func<T> comum. E o mais importante — quando usar cada um deles para não dar um tiro no pé.
\n\nBreve introdução: o que são Func e Expression?
\n\nFunc<T> é simplesmente um delegate. Uma função pronta para execução, código de máquina compilado. Você passa dados para ela — ela os processa. Rápido, eficiente, mas… opaco.
\n\nFunc<User, bool> isAdult = user => user.Age >= 18;\nbool result = isAdult(someUser); // executa instantaneamente
\n\nExpression<Func<T>> é uma árvore de expressão. Não é código, mas sua descrição. Você não pode simplesmente chamá-lo — precisa compilá-lo em um delegate (através de .Compile()) ou analisar sua estrutura.
\n\nExpression<Func<User, bool>> expr = user => user.Age >= 18;\n// Não pode simplesmente chamar: expr(someUser) — erro de compilação\nvar compiled = expr.Compile();\nbool result = compiled(someUser); // agora pode
\n\nParece um trabalho extra desnecessário? Mas é exatamente essa "descritividade" que abre a porta para a magia.
\n\nCenário 1: LINQ to Objects (trabalhando em memória)
\n\nQuando você trabalha com coleções em memória (por exemplo, List<T>), o LINQ to Objects usa Func<T>. Árvores de expressão não são necessárias aqui — tudo é executado no mesmo processo.
\n\nvar users = new List<User>();\nvar adults = users.Where(u => u.Age >= 18).ToList(); // Func<User, bool>
\n\nO desempenho é excelente: sem custos adicionais de análise de árvore. Se você trabalha apenas com dados locais — use Func sem medo. É mais rápido e mais simples.
\n\nCenário 2: LINQ to SQL / Entity Framework (trabalhando com BD)
\n\nE aqui começa a magia. Quando você escreve:
\n\nvar adults = dbContext.Users.Where(u => u.Age >= 18).ToList();
\n\nO Entity Framework não executa seu código C#. Em vez disso, ele analisa a árvore de expressão passada e a converte em uma consulta SQL:
\n\nSELECT * FROM Users WHERE Age >= 18
\n\nSe você tivesse passado um Func, o EF não conseguiria "olhar para dentro" — ele simplesmente carregaria TODOS os usuários da tabela e executaria o filtro em memória. Imagine: uma tabela com 10 milhões de registros — todos eles viajando pela rede até sua aplicação para serem filtrados no cliente.
\n\nExpression<Func<T>> permite que o EF (ou qualquer outro provedor IQueryable) leia sua condição, traduza para a linguagem do banco de dados (SQL, MongoDB query, OData, etc.) e execute o filtro no servidor.
\n\nDesempenho: benchmarks e números reais
\n\nVamos olhar para os números. Vamos pegar um exemplo simples: filtrar 100.000 usuários por idade.
\n\n// Opção 1: Func — carregamos todos, filtramos em memória\nvar allUsers = dbContext.Users.ToList(); // SELECT * FROM Users\nvar adults1 = allUsers.Where(u => u.Age >= 18).ToList();\n\n// Opção 2: Expression — filtramos no servidor\nvar adults2 = dbContext.Users.Where(u => u.Age >= 18).ToList(); // SELECT * FROM Users WHERE Age >= 18
\n\nResultados (aproximados, mas ilustrativos):
\n\n
Imagine: você está escrevendo código C#. Precisa passar uma condição de filtro para um método. O que escolher: Func<User, bool> ou Expression<Func<User, bool>>? Aparentemente a diferença é mínima — ambos recebem um usuário e retornam bool. Mas nos bastidores, esconde-se um universo inteiro de diferenças.
No Stack Overflow, essa pergunta acumulou milhares de visualizações — e não é à toa. Hoje vamos entender por que Expression<Func<T>> pode ser mais poderoso, mais rápido e mais seguro que o Func<T> comum. E o mais importante — quando usar cada um deles para não dar um tiro no pé.
\n\nBreve introdução: o que são Func e Expression?
\n\nFunc<T> é simplesmente um delegate. Uma função pronta para execução, código de máquina compilado. Você passa dados para ela — ela os processa. Rápido, eficiente, mas… opaco.
\n\nFunc<User, bool> isAdult = user => user.Age >= 18;\nbool result = isAdult(someUser); // executa instantaneamente\n\nExpression<Func<T>> é uma árvore de expressão. Não é código, mas sua descrição. Você não pode simplesmente chamá-lo — precisa compilá-lo em um delegate (através de .Compile()) ou analisar sua estrutura.
Expression<Func<User, bool>> expr = user => user.Age >= 18;\n// Não pode simplesmente chamar: expr(someUser) — erro de compilação\nvar compiled = expr.Compile();\nbool result = compiled(someUser); // agora pode\n\nParece um trabalho extra desnecessário? Mas é exatamente essa "descritividade" que abre a porta para a magia.
\n\nCenário 1: LINQ to Objects (trabalhando em memória)
\n\nQuando você trabalha com coleções em memória (por exemplo, List<T>), o LINQ to Objects usa Func<T>. Árvores de expressão não são necessárias aqui — tudo é executado no mesmo processo.
var users = new List<User>();\nvar adults = users.Where(u => u.Age >= 18).ToList(); // Func<User, bool>\n\nO desempenho é excelente: sem custos adicionais de análise de árvore. Se você trabalha apenas com dados locais — use Func sem medo. É mais rápido e mais simples.
\n\nCenário 2: LINQ to SQL / Entity Framework (trabalhando com BD)
\n\nE aqui começa a magia. Quando você escreve:
\n\nvar adults = dbContext.Users.Where(u => u.Age >= 18).ToList();\n\nO Entity Framework não executa seu código C#. Em vez disso, ele analisa a árvore de expressão passada e a converte em uma consulta SQL:
\n\nSELECT * FROM Users WHERE Age >= 18\n\nSe você tivesse passado um Func, o EF não conseguiria "olhar para dentro" — ele simplesmente carregaria TODOS os usuários da tabela e executaria o filtro em memória. Imagine: uma tabela com 10 milhões de registros — todos eles viajando pela rede até sua aplicação para serem filtrados no cliente.
\n\nExpression<Func<T>> permite que o EF (ou qualquer outro provedor IQueryable) leia sua condição, traduza para a linguagem do banco de dados (SQL, MongoDB query, OData, etc.) e execute o filtro no servidor.
\n\nDesempenho: benchmarks e números reais
\n\nVamos olhar para os números. Vamos pegar um exemplo simples: filtrar 100.000 usuários por idade.
\n\n// Opção 1: Func — carregamos todos, filtramos em memória\nvar allUsers = dbContext.Users.ToList(); // SELECT * FROM Users\nvar adults1 = allUsers.Where(u => u.Age >= 18).ToList();\n\n// Opção 2: Expression — filtramos no servidor\nvar adults2 = dbContext.Users.Where(u => u.Age >= 18).ToList(); // SELECT * FROM Users WHERE Age >= 18\n\nResultados (aproximados, mas ilustrativos):
\n- \n