一些在程序初始化时交给后台线程异步赋值的属性,完成set之前有可能UI就准备好了,也就是用户可以操作了,操作中如果需要get某个尚未set的属性,我希望代码阻塞,直到set后继续执行,当然如果get之前就已经set好了就无阻塞了。所以这种阻塞最多只用一次,为此专门弄个锁或者信号对象我觉得不地道,这种场景应该很典型,我想知道有没有比较优雅的方式(比如在这种属性上加个[xxx]特性之类)写这种属性?
一些在程序初始化时交给后台线程异步赋值的属性,完成set之前有可能UI就准备好了,也就是用户可以操作了,操作中如果需要get某个尚未set的属性,我希望代码阻塞,直到set后继续执行,当然如果get之前就已经set好了就无阻塞了。所以这种阻塞最多只用一次,为此专门弄个锁或者信号对象我觉得不地道,这种场景应该很典型,我想知道有没有比较优雅的方式(比如在这种属性上加个[xxx]特性之类)写这种属性?
类似这样?但是没有可以直接使用的Attribute,不过可以自己实现一套
using System;
using System.Threading.Tasks;
namespace Test6
{
class Program
{
static void Main(string[] args)
{
var t = new Test();
Console.WriteLine("[" + DateTime.Now.ToString("HH:mm:ss.fff") + "] " + t.NormalProperty);
//Console.WriteLine("[" + DateTime.Now.ToString("HH:mm:ss.fff") + "] " + t.LazyProperty);
Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss.fff")}] pretending doing something that will takes 3s");
System.Threading.Thread.Sleep(3000);
Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss.fff")}] something is done in 3s");
var lazyValue = t.LazyProperty;
Console.WriteLine("[" + DateTime.Now.ToString("HH:mm:ss.fff") + "] " + lazyValue);
Console.ReadLine();
}
}
public class Test
{
public string NormalProperty { get; set; }
public string LazyProperty
{
get
{
//if (GetLazyPropertyValue.IsCompleted)
//{
return GetLazyPropertyValue.Result;
//}
//else
//{
// return "LazyProperty uninitialized";
//}
}
}
public Test()
{
this.NormalProperty = "NormalProperty";
GetLazyPropertyValue.Start();
}
protected Task<string> GetLazyPropertyValue = new Task<string>(() =>
{
System.Threading.Thread.Sleep(5000);
return "LazyProperty initialized";
});
}
}
升了个级,加上了Attribute
using System;
using System.Dynamic;
using System.Reflection;
using System.Threading.Tasks;
namespace Test6
{
class Program
{
static void Main(string[] args)
{
dynamic t = new DynamicProxy(new Test());
Console.WriteLine("[" + DateTime.Now.ToString("HH:mm:ss.fff") + "] " + t.NormalProperty);
Console.WriteLine("[" + DateTime.Now.ToString("HH:mm:ss.fff") + "] " + t.LazyPropertyWithDefaultValue);
Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss.fff")}] pretending doing something that will takes 3s");
System.Threading.Thread.Sleep(3000);
Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss.fff")}] something is done in 3s");
var lazyValue = t.LazyProperty;
Console.WriteLine("[" + DateTime.Now.ToString("HH:mm:ss.fff") + "] " + lazyValue);
Console.WriteLine("[" + DateTime.Now.ToString("HH:mm:ss.fff") + "] " + t.LazyPropertyWithDefaultValue);
Console.ReadLine();
}
}
[LazyPropertyIncluded]
public class Test
{
public string NormalProperty { get; set; }
[LazyProperty(InitializerName = "InitLazyProperty")]
public string LazyProperty { get; }
[LazyProperty(HasDefaultValue =true, Defaultalue = "LazyPropertyWithDefaultValue Default Value")]
public string LazyPropertyWithDefaultValue { get; }
protected Task<string> InitLazyProperty = new Task<string>(() =>
{
Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss.fff")}] start initializing LazyProperty");
System.Threading.Thread.Sleep(5000);
return "LazyProperty initialized";
});
protected Task<string> InitLazyPropertyWithDefaultValue = new Task<string>(() =>
{
Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss.fff")}] start initializing LazyPropertyWithDefaultValue");
System.Threading.Thread.Sleep(5000);
return "LazyPropertyWithDefaultValue initialized";
});
public Test()
{
this.NormalProperty = "NormalProperty";
}
}
[AttributeUsage(AttributeTargets.Property)]
public class LazyPropertyAttribute : Attribute
{
public string InitializerName { get; set; }
public bool HasDefaultValue { get; set; }
public object Defaultalue { get; set; }
}
[AttributeUsage(AttributeTargets.Class)]
public class LazyPropertyIncludedAttribute : Attribute { }
public class DynamicProxy : DynamicObject
{
readonly object[] _sources;
public DynamicProxy(params object[] sources)
{
this._sources = sources;
// run init method for lazy properties
foreach (var src in this._sources)
{
var attr = src.GetType().GetCustomAttribute<LazyPropertyIncludedAttribute>();
if (attr != null)
{
foreach (var prop in src.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
var propAttr = prop.GetCustomAttribute<LazyPropertyAttribute>();
if (propAttr != null)
{
var initTask = propAttr.InitializerName ?? "Init" + prop.Name;
var task = src.GetType().GetField(initTask, BindingFlags.Instance | BindingFlags.NonPublic);
if (task != null)
{
((Task)task.GetValue(src)).Start();
}
else
{
throw new MethodAccessException("Cannot find initialization method [" + initTask + "] for lazy prop: " + prop.Name);
}
}
}
}
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
object propValue = null;
foreach (var src in this._sources)
{
var prop = src.GetType().GetProperty(binder.Name);
if (prop != null)
{
var propAttr = prop.GetCustomAttribute<LazyPropertyAttribute>();
if (propAttr == null)
{
propValue = prop.GetValue(src);
}
else
{
var initTask = propAttr.InitializerName ?? "Init" + prop.Name;
var task = src.GetType().GetField(initTask, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(src) as Task;
if (!task.IsCompleted && propAttr.HasDefaultValue)
{
propValue = propAttr.Defaultalue;
}
else
{
Task.WaitAll(task);
propValue = Convert.ChangeType(task.GetType().GetProperty("Result").GetValue(task), prop.PropertyType);
}
}
break;
}
}
if (propValue == null)
{
propValue = binder.Name + " from dynamic proxy";
}
result = propValue;
return true;
}
}
}
1 回答1.5k 阅读✓ 已解决
1 回答845 阅读
250 阅读
126 阅读
使用同步事件即可: