Почему мои связанные параметры все идентичны (используя Linq)? - PullRequest
5 голосов
/ 13 мая 2010

Когда я запускаю этот фрагмент кода:

string[] words = new string[] { "foo", "bar" };
var results = from row in Assets select row;
foreach (string word in words)
{
    results = results.Where(row => row.Name.Contains(word));
}

Я получаю этот SQL:

-- Region Parameters
DECLARE @p0 VarChar(5) = '%bar%'
DECLARE @p1 VarChar(5) = '%bar%'
-- EndRegion
SELECT ... FROM [Assets] AS [t0]
WHERE ([t0].[Name] LIKE @p0) AND ([t0].[Name] LIKE @p1)

Обратите внимание, что @p0 и @p1 оба bar, когда я хотел, чтобы они были foo и bar.

Я думаю, что Linq каким-то образом связывает ссылку на переменную word, а не ссылку на строку, на которую в данный момент ссылается word? Как лучше всего избежать этой проблемы?

(Также, если у вас есть предложения по улучшению названия этого вопроса, пожалуйста, оставьте его в комментариях.)

Обратите внимание, что я попробовал это и с обычным Linq, с теми же результатами (вы можете вставить это прямо в Linqpad):

string[] words = new string[] { "f", "a" };
string[] dictionary = new string[] { "foo", "bar", "jack", "splat" };
var results = from row in dictionary select row;
foreach (string word in words)
{
    results = results.Where(row => row.Contains(word));
}
results.Dump();

Сплин:

bar
jack
splat

1 Ответ

8 голосов
/ 13 мая 2010

Вы используете то, что называется «замыканием», что означает, что вы определяете анонимную функцию (вашу лямбду), которая использует локальную переменную в своем теле. В частности, вы «закрываете» переменную цикла word. Проблема, которая возникает с замыканиями, является следствием отложенного выполнения , что означает, что тело вашей лямбды не запускается, когда вы его определяете, а когда оно вызывается.

Из-за этого вы почти никогда не хотите закрывать переменную цикла. Из-за отложенного выполнения значение переменной в лямбде будет равно 1006 *, когда лямбда выполняется . Для лямбда-выражений, объявленных внутри цикла и вызванных вне его, это означает, что он всегда будет иметь последнее значение из цикла.

Чтобы противодействовать этому, используйте локальную переменную, объявленную внутри цикла. Это заставит его захватить значение в тот момент времени и передать новую переменную каждой лямбде, которая создается. Вот так:

string[] words = new string[] { "foo", "bar" }; 
var results = from row in Assets select row; 
foreach (string word in words) 
{ 
    string tempWord = word;

    results = results.Where(row => row.Name.Contains(tempWord)); 
} 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...