Вот более краткий ответ для людей, которые ищут быструю ссылку, а также некоторые примеры с использованием обещаний и асинхронных / ожидающих.
Начните с наивного подхода (который не работает) для функция, вызывающая асинхронный метод (в данном случае setTimeout
) и возвращает сообщение:
function getMessage() {
var outerScopeVar;
setTimeout(function() {
outerScopeVar = 'Hello asynchronous world!';
}, 0);
return outerScopeVar;
}
console.log(getMessage());
undefined
регистрируется в этом случае, поскольку getMessage
возвращается до вызова setTimeout
и обновления outerScopeVar
.
Двумя основными способами его решения являются обратные вызовы и обещания:
Обратные вызовы
Здесь изменение состоит в том, что getMessage
принимает параметр callback
, который будет вызываться для доставки результатов обратно к вызывающему коду после его доступности.
function getMessage(callback) {
setTimeout(function() {
callback('Hello asynchronous world!');
}, 0);
}
getMessage(function(message) {
console.log(message);
});
Обещания предоставляют альтернативу который является более гибким, чем обратные вызовы, потому что их можно комбинировать естественно для координации нескольких асинхронных операций. Стандартная реализация Promises / A + изначально представлена в node.js (0.12+) и многих текущих браузерах, но также реализована в таких библиотеках, как Bluebird и Q .
function getMessage() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('Hello asynchronous world!');
}, 0);
});
}
getMessage().then(function(message) {
console.log(message);
});
jQuery Отложенные
jQuery предоставляет функциональные возможности, аналогичные обещаниям с его отсрочкой.
function getMessage() {
var deferred = $.Deferred();
setTimeout(function() {
deferred.resolve('Hello asynchronous world!');
}, 0);
return deferred.promise();
}
getMessage().done(function(message) {
console.log(message);
});
async / await
Если ваша среда JavaScript включает поддержку async
и await
(например, Node.js 7.6+), то вы может использовать обещания синхронно в пределах функций async
:
function getMessage () {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('Hello asynchronous world!');
}, 0);
});
}
async function main() {
let message = await getMessage();
console.log(message);
}
main();
Когда вы вызываете Control.Controls
, он возвращает только элементы управления на самом удаленном уровне. Он не будет рекурсивно спускаться в любые элементы управления контейнерами, в которых содержатся другие элементы управления.
Если ваши элементы управления находятся в другом контейнере, вам нужно будет использовать свойство .Controls
этого контейнера.
В качестве альтернативы вы можете обобщить его, написав метод рекурсивного возврата всех элементов управления из родительского и всех его дочерних элементов, например:
public IEnumerable<Control> AllControls(Control container)
{
foreach (Control control in container.Controls)
{
yield return control;
foreach (var innerControl in AllControls(control))
yield return innerControl;
}
}
Затем вы можете использовать это вместо Control.Controls следующим образом:
private void test() // Assuming this is a member of a Form other class derived from Control
{
var textboxesWithFilledTag =
AllControls(this).OfType<TextBox>()
.Where(tb => (string) tb.Tag == "Filled");
foreach (var textbox in textboxesWithFilledTag)
Debug.WriteLine(textbox.Text);
}
Как говорится в комментарии, я предполагаю, что метод test()
является членом вашей Формы или другого класса, производного от Control. Если это не так, вам нужно передать ему родительский элемент управления:
private void test(Control container)
{
var textboxesWithFilledTag =
AllControls(container).OfType<TextBox>()
.Where(tb => (string) tb.Tag == "Filled");
foreach (var textbox in textboxesWithFilledTag)
Debug.WriteLine(textbox.Text);
}
Следующий метод имеет идентичные результаты с приведенным выше, для справки (и более читаемым IMHO):
private void test(Control container)
{
foreach (var textbox in AllControls(container).OfType<TextBox>())
if ((string)textbox.Tag == "Filled")
Debug.WriteLine(textbox.Text);
}
Для вашего кода обработчик щелчка на кнопке может выглядеть примерно так:
void button1_Click(object sender, EventArgs e)
{
foreach (var c in AllControls(this).OfType<TextBox>())
{
if ((string) c.Tag == "Filled")
{
// Here is where you put your code to do something with Textbox 'c'
}
}
}
Обратите внимание, что вам также нужен метод AllControls()
, конечно.
Вы устанавливаете свойство tag для себя. Это свойство свойства string
. Вы можете попробовать следующее:
if (c.Tag == "Filled")
{
Console.WriteLine(c.Name);
}
, если вы хотите проверить, что текстовое поле не пустое, вы можете просто попробовать следующее:
if (c.Text.Trim().Length == 0)
{
Console.WriteLine(c.Name);
}
Чтобы получить все элементы управления (а не только прямые дочерние элементы формы), вы можете использовать этот рекурсивный Linq
Func<Control, IEnumerable<Control>> allControls = null;
allControls = c => new Control[] { c }
.Concat(c.Controls.Cast<Control>()
.SelectMany(x=>allControls(x)));
. Теперь вы можете фильтровать TextBoxes
var tbs = allControls(this).OfType<TextBox>()
.Where(t=>(string)t.Tag=="Filled")
.ToList();
Лучше использовать if (c is TextBox)
. Кроме того, если вы хотите знать, почему ваш код разбивается, используйте try / catch
Это может быть проще:
foreach ( TextBox tb in this.Controls.OfType<TextBox>())
{
if ((string)tb.Tag == "Filled")
// .....
}
Я бы рекомендовал использовать следующий синтаксис:
foreach (Control c in Controls)
if (c is TextBox)