具有动态编程的 FrameworkElement 和 FrameworkContentElement 的通用接口
Common Interface for FrameworkElement and FrameworkContentElement with dynamic programming
我试着简而言之:FrameworkElement 和 FrameworkContentElement 共享很多相同的东西 API 但没有通用的接口。只有 DependencyObject 作为基础 class.
我遇到过 this implementation of IFrameworkElement,它手动添加了一个接口和两个包装器 classes。现在这段代码是在 .NET 3.5 中实现的,作者评论说在 .NET 4 中使用动态编程会容易得多:
The actual code is very simple, but about 624 lines long for each element. (It will be a much simpler one line implementation in a dynamic language - C# 4.0 is coming :) ).
我很好奇它的实现会是什么样子。我假设它会归结为 IFrameworkElement
的动态实现,并阅读有关 ExpandoObject
和 DynamicObject
的内容,看看我是否可以自己实现,但我有点难过。我想可以编写 DynamicObject 的自定义实现 - 但这不是单行代码。这真的可以通过动态规划变得 that 容易吗?我什至不需要成为一个班轮,我可以用 10 甚至 100 行而不是原来的 1250 行。
我在想这样的事情:
// Example, not working:
public IFrameworkElement AsFrameworkElement(FrameworkElement ele)
{
dynamic ife = ele as IFrameworkElement;
return ife;
}
IFrameworkElement frameworkElement = AsFrameworkElement(new Button());
frameworkElement.DataContext = "Whatever";
IFrameworkElement frameworkContentElement = AsFrameworkElement(new Paragraph());
frameworkContentElement.DataContext = "Whatever again";
看博客原代码:
var dataContect = "DataContext";
var frameworkElement = sender as FrameworkElement;
if ( frameworkElement != null )
{
frameworkElement.DataContext = dataContect;
}
else
{
var frameworkContentElement = sender as FrameworkContentElement;
if ( frameworkContentElement != null )
{
frameworkContentElement.DataContext = dataContect;
}
}
会变成
var dataContext = "DataContext"
dynamic element = sender;
element.DataContext = dataContext;
到此为止。在运行时,将通过反射搜索名称为 DataContext
的 属性(注意:当涉及到动态类型列表时,速度可能会非常慢),然后调用它。
注意:如果 属性 不存在,将抛出 RuntimeBinderException
。您可以在最后一行添加一些 try...catch。
我不知道那篇文章的作者到底是什么意思(也许我们应该问him/her),但我想它需要的不仅仅是一行代码。
重点是 dynamic keyword (new with 4.0 .NET) allows the so called duck typing.
文章的作者必须编写 2 个包装器 classes 才能使 FrameworkElement
和 FrameworkContentElement
实现 IFrameworkElement
接口。
现在有了 dynamic
键,我们可以只写 class(为了保持我们界面 的舒适度 )。
public interface IFrameworkElement
{
/* Let's suppose we have just one property, since it is a sample */
object DataContext
{
get;
set;
}
}
public class FrameworkElementImpl : IFrameworkElement
{
private readonly dynamic target;
public FrameworkElementImpl(dynamic target)
{
this.target = target;
}
public object DataContext
{
get
{
return target.DataContext;
}
set
{
target.DataContext = value;
}
}
}
public static class DependencyObjectExtension
{
public static IFrameworkElement AsIFrameworkElement(this DependencyObject dp)
{
if (dp is FrameworkElement || dp is FrameworkContentElement)
{
return new FrameworkElementImpl(dp);
}
return null;
}
}
所以现在我们可以在代码中这样写:
System.Windows.Controls.Button b = new System.Windows.Controls.Button();
IFrameworkElement ife = b.AsIFrameworkElement();
ife.DataContext = "it works!";
Debug.Assert(b.DataContext == ife.DataContext);
现在,如果您不想编写包装器(或您希望的代理)class(即我们示例中的 FrameworkElementImpl
),有一些库可以为您完成(impromptu-interface or Castle DynamicProxy).
您可以找到here一个使用 Castle DynamicProxy 的非常简单的示例:
public class Duck
{
public void Quack()
{
Console.WriteLine("Quack Quack!");
}
public void Swim()
{
Console.WriteLine("Swimming...");
}
}
public interface IQuack
{
void Quack();
}
public interface ISwimmer
{
void Swim();
}
public static class DuckTypingExtensions
{
private static readonly ProxyGenerator generator = new ProxyGenerator();
public static T As<T>(this object o)
{
return generator.CreateInterfaceProxyWithoutTarget<T>(new DuckTypingInterceptor(o));
}
}
public class DuckTypingInterceptor : IInterceptor
{
private readonly object target;
public DuckTypingInterceptor(object target)
{
this.target = target;
}
public void Intercept(IInvocation invocation)
{
var methods = target.GetType().GetMethods()
.Where(m => m.Name == invocation.Method.Name)
.Where(m => m.GetParameters().Length == invocation.Arguments.Length)
.ToList();
if (methods.Count > 1)
throw new ApplicationException(string.Format("Ambiguous method match for '{0}'", invocation.Method.Name));
if (methods.Count == 0)
throw new ApplicationException(string.Format("No method '{0}' found", invocation.Method.Name));
var method = methods[0];
if (invocation.GenericArguments != null && invocation.GenericArguments.Length > 0)
method = method.MakeGenericMethod(invocation.GenericArguments);
invocation.ReturnValue = method.Invoke(target, invocation.Arguments);
}
}
如您所见,在这种情况下,只需几行代码即可获得与作者使用
获得的相同结果
about 624 lines [...] for each element
我试着简而言之:FrameworkElement 和 FrameworkContentElement 共享很多相同的东西 API 但没有通用的接口。只有 DependencyObject 作为基础 class.
我遇到过 this implementation of IFrameworkElement,它手动添加了一个接口和两个包装器 classes。现在这段代码是在 .NET 3.5 中实现的,作者评论说在 .NET 4 中使用动态编程会容易得多:
The actual code is very simple, but about 624 lines long for each element. (It will be a much simpler one line implementation in a dynamic language - C# 4.0 is coming :) ).
我很好奇它的实现会是什么样子。我假设它会归结为 IFrameworkElement
的动态实现,并阅读有关 ExpandoObject
和 DynamicObject
的内容,看看我是否可以自己实现,但我有点难过。我想可以编写 DynamicObject 的自定义实现 - 但这不是单行代码。这真的可以通过动态规划变得 that 容易吗?我什至不需要成为一个班轮,我可以用 10 甚至 100 行而不是原来的 1250 行。
我在想这样的事情:
// Example, not working:
public IFrameworkElement AsFrameworkElement(FrameworkElement ele)
{
dynamic ife = ele as IFrameworkElement;
return ife;
}
IFrameworkElement frameworkElement = AsFrameworkElement(new Button());
frameworkElement.DataContext = "Whatever";
IFrameworkElement frameworkContentElement = AsFrameworkElement(new Paragraph());
frameworkContentElement.DataContext = "Whatever again";
看博客原代码:
var dataContect = "DataContext";
var frameworkElement = sender as FrameworkElement;
if ( frameworkElement != null )
{
frameworkElement.DataContext = dataContect;
}
else
{
var frameworkContentElement = sender as FrameworkContentElement;
if ( frameworkContentElement != null )
{
frameworkContentElement.DataContext = dataContect;
}
}
会变成
var dataContext = "DataContext"
dynamic element = sender;
element.DataContext = dataContext;
到此为止。在运行时,将通过反射搜索名称为 DataContext
的 属性(注意:当涉及到动态类型列表时,速度可能会非常慢),然后调用它。
注意:如果 属性 不存在,将抛出 RuntimeBinderException
。您可以在最后一行添加一些 try...catch。
我不知道那篇文章的作者到底是什么意思(也许我们应该问him/her),但我想它需要的不仅仅是一行代码。 重点是 dynamic keyword (new with 4.0 .NET) allows the so called duck typing.
文章的作者必须编写 2 个包装器 classes 才能使 FrameworkElement
和 FrameworkContentElement
实现 IFrameworkElement
接口。
现在有了 dynamic
键,我们可以只写 class(为了保持我们界面 的舒适度 )。
public interface IFrameworkElement
{
/* Let's suppose we have just one property, since it is a sample */
object DataContext
{
get;
set;
}
}
public class FrameworkElementImpl : IFrameworkElement
{
private readonly dynamic target;
public FrameworkElementImpl(dynamic target)
{
this.target = target;
}
public object DataContext
{
get
{
return target.DataContext;
}
set
{
target.DataContext = value;
}
}
}
public static class DependencyObjectExtension
{
public static IFrameworkElement AsIFrameworkElement(this DependencyObject dp)
{
if (dp is FrameworkElement || dp is FrameworkContentElement)
{
return new FrameworkElementImpl(dp);
}
return null;
}
}
所以现在我们可以在代码中这样写:
System.Windows.Controls.Button b = new System.Windows.Controls.Button();
IFrameworkElement ife = b.AsIFrameworkElement();
ife.DataContext = "it works!";
Debug.Assert(b.DataContext == ife.DataContext);
现在,如果您不想编写包装器(或您希望的代理)class(即我们示例中的 FrameworkElementImpl
),有一些库可以为您完成(impromptu-interface or Castle DynamicProxy).
您可以找到here一个使用 Castle DynamicProxy 的非常简单的示例:
public class Duck
{
public void Quack()
{
Console.WriteLine("Quack Quack!");
}
public void Swim()
{
Console.WriteLine("Swimming...");
}
}
public interface IQuack
{
void Quack();
}
public interface ISwimmer
{
void Swim();
}
public static class DuckTypingExtensions
{
private static readonly ProxyGenerator generator = new ProxyGenerator();
public static T As<T>(this object o)
{
return generator.CreateInterfaceProxyWithoutTarget<T>(new DuckTypingInterceptor(o));
}
}
public class DuckTypingInterceptor : IInterceptor
{
private readonly object target;
public DuckTypingInterceptor(object target)
{
this.target = target;
}
public void Intercept(IInvocation invocation)
{
var methods = target.GetType().GetMethods()
.Where(m => m.Name == invocation.Method.Name)
.Where(m => m.GetParameters().Length == invocation.Arguments.Length)
.ToList();
if (methods.Count > 1)
throw new ApplicationException(string.Format("Ambiguous method match for '{0}'", invocation.Method.Name));
if (methods.Count == 0)
throw new ApplicationException(string.Format("No method '{0}' found", invocation.Method.Name));
var method = methods[0];
if (invocation.GenericArguments != null && invocation.GenericArguments.Length > 0)
method = method.MakeGenericMethod(invocation.GenericArguments);
invocation.ReturnValue = method.Invoke(target, invocation.Arguments);
}
}
如您所见,在这种情况下,只需几行代码即可获得与作者使用
获得的相同结果about 624 lines [...] for each element