я хочу реализовать «делать, когда» модель, которая позволяет мне не писать 3 вещи:Как получить уникальный идентификатор функции лямбда для шаблона «сделать один раз»?
- объявляющие вар первый = истинный
- если (первый) Do (...) заявление внутри повторяемого блока кода
- первое = ложное назначение внутри повторного блока кода
Я также хочу, чтобы избежать обходных путей, как эти:
- вручную сохраняя и прохождение уникальной идентификации в функцию Do
- определения раз контекста переменных несколько раз
Так что мой код должен выглядеть так просто, как это:
using(var once = new Once())
foreach(var it in new[]{1,2,3}){
once.Do(()=>Console.Write("It should write once and only once"));
Console.Write("It should write 3 times");
foreach(var it2 in new[]{4,5}){
once.Do(()=>Console.Write("Inner loop should write once and only once"));
Console.Write("It should write 6 times");
}
}
или это:
using(var once = new Once())
foreach(var it in new[]{1,2,3}){
once.Do(()=>Console.Write("It should write once and only once"));
Console.Write("It should write 3 times");
once.Do(()=>Console.Write("It should write once, but only after first instance (out of 3) of previous write."));
foreach(var it2 in new[]{4,5}){
once.Do(()=>Console.Write("Inner loop should write once and only once"));
Console.Write("It should write 6 times");
once.Do(()=>Console.Write("It should write once, but only after first instance (out of 6) of previous write."));
}
Console.Write("Repeated ending should appear 3 times");
}
Если я использую ObjectIDGenerator, он не решает мою проблему, потому что он дает разные действия Id for Action навсегда y в этой реализации:
public class Once : IDisposable{
HashSet<long> passed;
static ObjectIDGenerator idgen = new ObjectIDGenerator();
public Once(){
passed = passed.New();
}
public bool Do(Action act){
if(act != null){
bool firstTime;
var id = idgen.GetId(act,out firstTime);
if(!passed.Contains(id)){
act();
passed.Add(id);
return true;
}
else
return false;
}
else
return false;
}
void IDisposable.Dispose() {
passed.Clear();
}
}
Как получить уникальный идентификатор прошедшей функции лямбда? Я думаю, что это можно сделать, пройдя его как Дерево выражений и вычисляя хэш или иначе сериализуя его во что-то, что можно поместить в HashSet.
Но я бы предпочел, чтобы в исходном коде можно было найти имя файла и номер строки, который определяет эту лямбда-функцию и использует ее как идентификатор. Это легко разрешило бы проблему разных мест определения, имеющих разные уникальные идентификаторы, даже если было установлено значение &.
Я предполагаю, что одним из способов было бы использовать ObjectIDGenerator для объекта дерева выражений, который представляет эту функцию лямбда. Будет ли ObjectIDGenerator возвращать тот же идентификатор для этого дерева выражений?
Другой пример: как реализовать только класса и включают в себя вложенные петли переменной it2 в once.Do invokation так, что можно было бы назвать лишь дважды - один раз для IT2 = 4 и один раз для IT2 = 5:
using(var once = new Once())
foreach(var it in new[]{1,2,3}){
once.Do(()=>Console.Write("It should write once and only once"));
Console.Write("It should write 3 times");
foreach(var it2 in new[]{4,5}){
once.Do(()=>Console.Write("Inner loop should write twice and only twice: {0}", it2));
Console.Write("It should write 6 times");
}
}
Другой пример: как реализовать один раз класс и включить внешнюю переменную цикла в один раз. Включение, чтобы оно было вызвано только 3 раза - один раз для него = 1, один раз для него = 2 и один раз для него = 3:
using(var once = new Once())
foreach(var it in new[]{1,2,3}){
once.Do(()=>Console.Write("It should write once and only once"));
Console.Write("It should write 3 times");
foreach(var it2 in new[]{4,5}){
once.Do(()=>Console.Write("Inner loop should write 3 times and only 3 times: {0}", it));
Console.Write("It should write 6 times");
}
}
Другое уточнение: Если есть вторая функция лямбда, которая определена где-то еще в моем коде, я хочу, чтобы у нее был другой идентификатор, даже если это копиявставки первой лямбда и имеет идентичную реализацию.
using(var once = new Once())
foreach(var it in new[]{1,2,3}){
once.Do(()=>Console.Write("It should write twice because it's defined in different lines of code"));
once.Do(()=>Console.Write("It should write twice because it's defined in different lines of code"));
Console.Write("It should write 3 times");
}
сейчас думать об этом, в идеальном растворе я бы исключить из ид все, что передается в качестве одного из явных параметров, как этот (x,y,z,...)=>...
и включает в себя значение все захваченного контекстных переменных, на которые ссылаются этой лямбда-функцией. Так
using(var once = new Once())
foreach(var it in new[]{1,2,3}){
once.Do((arg)=>Console.Write("It should write once {0}",arg));
once.Do(()=>Console.Write("It should write 3 times {0}",it));
Console.Write("It should write 3 times");
}
Или может быть инверсия лучше:
using(var once = new Once())
foreach(var it in new[]{1,2,3}){
once.Do(()=>Console.Write("It should write once {0}",it));
once.Do((arg)=>Console.Write("It should write 3 times {0}",arg));
Console.Write("It should write 3 times");
}
В любом случае цель 2 последних примеров, чтобы показать, как быть в состоянии чисто контролировать то, что входит в определение уникальности, а что нет ,
адресация решение от Джона здесь еще одно уточнение:
Я хочу, чтобы сохранить определение один раз и не один раз действия в той же последовательности, как если бы они были все не один раз, так что порядок появления, б, в моем исходном коде не должен быть изменена, если я решу писать б только один раз или нет:
using(var once = new Once())
foreach(var it in new[]{1,2,3}){
Console.Write("a");
Console.Write("b");
Console.Write("c");
}
не должен быть изменен:
using(var once = new Once())
foreach(var it in new[]{1,2,3}){
Console.Write("a");
once.Do(()=>Console.Write("b"));
Console.Write("c");
}
Реалистичных е xample - представьте себе таблицу со многими полями возможных сумм, представьте, что я не могу сгенерировать скрипт и выполнить его в одной партии (что имеет место с Azure и, возможно, другими облачными базами данных), также представьте, что мы также определяем метод AfterFirst в нашем классе Once:
using(var tblOnce = new Once())
foreach(var tbl in db.Tables)
using(var fldOnce = new Once())
foreach(var fld in tbl.Fields){
fldOnce.Do( ()=>conn.Exec(" CREATE TABLE {0}({1} {2})",tbl.Name, fld.Name, fld.SqlType));
if(fld.Name.EndsWith("Amount"))
fldOnce.Do( ()=>conn.Exec(" ALTER TABLE {0} ADD Total money", tbl.Name));
fldOnce.AfterFirst(()=>conn.Exec(" ALTER TABLE {0} ADD {1} {2}", tbl.Name, fld.Name, fld.SqlType));
if(fld.PrimaryKey)
fldOnce.Do( ()=>conn.Exec(" ALTER TABLE {0} ADD CONSTRAINT PK_{0}_{1} PRIMARY KEY CLUSTERED({1})", tbl.Name, fld.Name));
fldOnce.Do(()=>
tblOnce.Do( ()=>conn.Exec(" CREATE TABLE Tables (name varchar(50))"));
conn.Exec(" INSERT tables (name) select " + tbl.Name);
);
}
Просто вызовите 'Take (1)' на последовательности, чтобы не воздействовать на любых предметах после первого. – Servy
Если уникальная идентификация каждого действия проблематична, вы можете просто передать в контекстную строку (например, «context1», «context2» и т. Д.) Вашу функцию Do. Простой hashset мог отслеживать контексты, которые уже выполнялись один раз. Возможно, это не идеальное решение, но попытка хешировать дерево выражений вашего действия, чтобы идентифицировать его, просто кажется мне тяжелым. – RogerN
Учитывая характер моего вопроса с наличием простого решения с булевыми флагами, я не ищу «не идеальное решение». – alpav