Tre*_*exx 5 .net c# expression
我有一个我想定期调用的方法。该方法有两个 DateTime 参数。
public static object PrintDateTimes(DateTime fromDate, DateTime toDate)
{
Console.WriteLine(
string.Format("PrintDateTimes:\t{0} - {1} :: {2}", fromDate.ToLongTimeString(), toDate.ToLongTimeString(), Program.StartDateTimeString));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
该类Item提供方法Refresh和属性callback来存储应该被调用的方法。
public class Item
{
public Expression<Func<object>> callback { get; set; }
public void Refresh()
{
this.Refresh(this.callback);
}
public void Refresh(Expression<Func<object>> callback)
{
MethodCallExpression methodBody = (MethodCallExpression)callback.Body;
bool fromDateFound = false;
bool toDateFound = false;
foreach (var arg in methodBody.Arguments) {
if (arg.Type == typeof(DateTime)) {
if (fromDateFound && !toDateFound) {
DateTime fromDate = Convert.ToDateTime(Expression.Lambda(arg).Compile().DynamicInvoke());
Console.WriteLine("newFromDate\t" + fromDate);
toDateFound = true;
} else if (!fromDateFound) {
DateTime toDate = Convert.ToDateTime(Expression.Lambda(arg).Compile().DynamicInvoke());
fromDateFound = true;
Console.WriteLine("newToDate\t" + toDate);
}
}
}
callback.Compile().Invoke();
Console.WriteLine();
}
Run Code Online (Sandbox Code Playgroud)
主要方法看起来像这样
public static string StartDateTimeString;
public static void Main()
{
DateTime now = DateTime.Now;
DateTime fromDate = DateTime.Now.AddHours(-0.5);
DateTime toDate = DateTime.Now.AddHours(1);
Program.StartDateTimeString = DateTime.Now.ToLongTimeString();
Item item = new Item();
// this is important
AssignCallback(fromDate, toDate, item);
timer = new Timer(timerCallback, item, Timeout.Infinite, Timeout.Infinite);
timer.Change(0, Timeout.Infinite);
while (true) ;
}
public static void AssignCallback(DateTime fromDate, DateTime toDate, Item item)
{
item.callback = () => Program.PrintDateTimes(fromDate, toDate);
}
Run Code Online (Sandbox Code Playgroud)
最后是定时器回调
private static void timerCallback(object state)
{
Item i = state as Item;
i.Refresh();
Console.WriteLine("Refresh at:\t" + DateTime.Now.ToLongTimeString());
timer.Change(TimeSpan.FromSeconds(3), Timeout.InfiniteTimeSpan);
}
Run Code Online (Sandbox Code Playgroud)
这是执行代码时的输出(并等待几秒钟)
Refresh at: 10:53:58
newToDate 10:23:58
newFromDate 11:53:58
PrintDateTimes: 10:23:58 - 11:53:58 :: 10:53:58
Refresh at: 10:54:01
newToDate 10:23:58
newFromDate 11:53:58
PrintDateTimes: 10:23:58 - 11:53:58 :: 10:53:58
Refresh at: 10:54:04
newToDate 10:23:58
newFromDate 11:53:58
PrintDateTimes: 10:23:58 - 11:53:58 :: 10:53:58
Refresh at: 10:54:07
newToDate 10:23:58
newFromDate 11:53:58
PrintDateTimes: 10:23:58 - 11:53:58 :: 10:53:58
Run Code Online (Sandbox Code Playgroud)
到目前为止一切顺利,但这不是所需的输出。调用回调时,将使用初始调用的 DateTime 值 - 但我希望在每次调用时再次评估它们,而不仅仅是传递它们的旧值。
所需的输出是这个(注意秒)
Refresh at: 10:53:58
newToDate 10:23:58
newFromDate 11:53:58
PrintDateTimes: 10:23:58 - 11:53:58 :: 10:53:58
Refresh at: 10:54:01
newToDate 10:23:01
newFromDate 11:53:01
PrintDateTimes: 10:23:01 - 11:53:01 :: 10:53:58
Refresh at: 10:54:04
newToDate 10:23:04
newFromDate 11:53:04
PrintDateTimes: 10:23:04 - 11:53:04 :: 10:53:58
Refresh at: 10:54:07
newToDate 10:23:07
newFromDate 11:53:07
PrintDateTimes: 10:23:07 - 11:53:07 :: 10:53:58
Run Code Online (Sandbox Code Playgroud)
我注意到argin的类型foreach (var arg in methodBody.Arguments) {是FieldExpression- afaik - 意味着它是恒定的。
如果我删除调用AssignCallback并直接分配回调,那么item.callback = () => PrintDateTimes(DateTime.Now.AddHours(-0.5), DateTime.Now.AddHours(1));它实际上可以工作。
可能因为 thenarg将是 type InstanceMethodCallExpressionN,所以没有常量。似乎将在每次调用时进行评估,而不是使用常量值传递。
有没有办法在每次调用时重新评估这些参数,以便正确更新值?
如果我正确地遵循您的示例,您的Refresh方法似乎不会更新值 - 它会选择您分配给它的任何表达式并一遍又一遍地重新评估它。
所以看来您缺少的是fromDate, toDate您的timerCallback. 我假设您有理由在那里使用表达式,但如果没有 - 一个简单的委托实际上可能会用更少的代码(和性能损失)来完成这项工作:
public class Item
{
public Func<DateTime, DateTime, object> callbackFunc { get; set; } // to illustrate alternative point
public Expression<Func<object>> callback { get; set; }
public void Refresh()
{
Refresh(callback);
}
public void RefreshFunc(DateTime fromDate, DateTime toDate) // alternative execution path - see timerCallback()
{
callbackFunc(fromDate, toDate);
}
public void Refresh(Expression<Func<object>> callback)
{
//your code with no change where
}
}
class Program
{
public static object PrintDateTimes(DateTime fromDate, DateTime toDate)
{
Console.WriteLine($"PrintDateTimes:\t{fromDate.ToLongTimeString()} - {toDate.ToLongTimeString()} :: {Program.StartDateTimeString}");
return 0;
}
public static string StartDateTimeString;
private static Timer timer;
public static void Main()
{
DateTime now = DateTime.Now;
DateTime fromDate = now.AddHours(-0.5);
DateTime toDate = now.AddHours(1);
Program.StartDateTimeString = now.ToLongTimeString();
Item item = new Item();
// this is important
AssignCallback(fromDate, toDate, item);
timer = new Timer(timerCallback, item, Timeout.Infinite, Timeout.Infinite);
timer.Change(0, Timeout.Infinite);
while (true) ;
}
public static void AssignCallback(DateTime fromDate, DateTime toDate, Item item)
{
item.callback = () => Program.PrintDateTimes(fromDate, toDate);
}
private static void timerCallback(object state)
{
Item i = state as Item;
// update the dates
DateTime now = DateTime.Now;
DateTime fromDate = now.AddHours(-0.5);
DateTime toDate = now.AddHours(1);
// call refresh delegate - you proably don't even need to wrap it in a method: i.callbackFunc(toDate, fromDate)
i.RefreshFunc(fromDate, toDate);
// or use your old method, but update the expression so it gets new values
//AssignCallback(fromDate, toDate, i); // i'm not sure if you can do this in your actual code, but this basically reassigns the expression so it works
//i.Refresh();
Console.WriteLine("Refresh at:\t" + now.ToLongTimeString());
timer.Change(TimeSpan.FromSeconds(3), Timeout.InfiniteTimeSpan);
}
}
Run Code Online (Sandbox Code Playgroud)