引言
在 MSDN 中对 System.Linq.Enumerable 类的 AsEnumerable 方法相关描述如下所示:
Enumerable.AsEnumerable<TSource> 方法: 返回类型化为 IEnumerable<T> 的输入。命名空间: System.Linq
程序集: System.Core (在 System.Core.dll 中)
语法: public static IEnumerable AsEnumerable(this IEnumerable source)
备注:
除了将 source 的编译时类型从实现 IEnumerable 的类型更改为 IEnumerable 本身之外,AsEnumerable(IEnumerable) 方法没做其他任何事。
The AsEnumerable(IEnumerable) method has no effect other than to change the compile-time type of source from a type that implements IEnumerable to IEnumerable itself.
AsEnumerable(IEnumerable) 可用于在序列实现 IEnumerable 时在查询实现之间进行选择,同时它还具有一组不同的可用公共查询方法。例如,假设给定一个泛型类 Table,该类实现 IEnumerable 并且具有自己的方法(如 Where、Select 和 SelectMany),则调用 Where 将调用 Table 的公共 Where 方法。表示数据库表的 Table 类型可能具有一个 Where 方法,该方法将谓词参数作为表达式目录树,并将该树转换为 SQL 以供远程执行。如果不需要远程执行(例如,谓词调用本地方法),则 AsEnumerable 方法可用于隐藏自定义方法,并使标准查询运算符变为可用。
AsEnumerable(IEnumerable) can be used to choose between query implementations when a sequence implements IEnumerable but also has a different set of public query methods available.For example, given a generic class Table that implements IEnumerable and has its own methods such as Where, Select, and SelectMany, a call to Where would invoke the public Where method of Table.A Table type that represents a database table could have a Where method that takes the predicate argument as an expression tree and converts the tree to SQL for remote execution.If remote execution is not desired, for example because the predicate invokes a local method, the AsEnumerable method can be used to hide the custom methods and instead make the standard query operators available.
实际上,这个方法只有一条语句,就是原样返回它的唯一参数:
- reutrn source
使用 .NET Reflector 查看 .NET Framework 4.5 Class Library,就很清楚了:
测试程序
这种只是原样返回它的唯一参数的方法有什么用呢?让我们来看一个例子吧:
2 using System.Linq;
3 using System.Collections.Generic;
4
5 namespace Skyiv.Test
6 {
7 static class LinqTester
8 {
9 class Firster<T> : List<T> { public T First() { return default(T); } }
10 static void Test<T>(Firster<T> x) { Console.WriteLine("F:" + x.First()); }
11 static void Test<T>(List<T> x) { Console.WriteLine("L:" + x.First()); }
12 static void Test<T>(IEnumerable<T> x) { Console.WriteLine("I:" + x.First()); }
13
14 static void Main()
15 {
16 var a = new Firster<int> { 2, 3, 5 };
17 Test(a);
18 Test(a as List<int>);
19 Test(a.AsEnumerable());
20 }
21 }
22 }
在上述程序中:
在 Arch Linux 的 Mono 环境下编译和运行:
work$ dmcs LinqTester.cs && mono LinqTester.exeF:0
L:2
I:2
上述运行结果解释如下:
如果在上述程序中,分别进行以下操作:
- 删除第 10 行的语句
- 删除第 11 行的语句
- 删除第 10 行和第 11 行的语句
再重新编译和运行,分别会有什么结果呢?
另外一个测试程序
前面的测试程序引用了 System.Linq 和 System.Collections.Generic 命名空间,涉及到的东东比较复杂。下面我们来一个简单点的测试程序:
2
3 namespace Skyiv.Test
4 {
5 class Thing { public string GetId() { return "Thing"; } }
6
7 static class Extensions
8 {
9 public static object AsObject(this object x) { return x; }
10 public static string
要绑定的数据结构如下,一个Category包含多个SubCategory:
{
public string Name { get; set; }
public ObservableCollection<SubCategory> SubCategories { get; set; }
}
public class SubCategory
{
public int Id { get; set; }
public string SubCategoryName { get; set; }
}
Code-behind初始化测试数据:
{
public ObservableCollection<Category> CategorieCollection { get; set; }
public AssetEditor()
{
InitializeComponent();
CategorieCollection = new ObservableCollection<Category>();
CategorieCollection.Add(new Category()
{
Name = "Cate1",
SubCategories = new ObservableCollection<SubCategory>()
});
CategorieCollection.Add(new Category()
{
Name = "Cate2",
SubCategories = new ObservableCollection<SubCategory>()
});
CategorieCollection[0].SubCategories.Add(new SubCategory()
{
Id = 0,
SubCategoryName = "sub1"
});
CategorieCollection[1].SubCategories.Add(new SubCategory()
{
Id = 0,
SubCategoryName = "sub2"
});
cmbCategoryName.DataContext = CategorieCollection;
}
}
XAML里绑定:
ItemsSource="{Binding}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedItem="{Binding Path=SubCategories}" />
<ComboBox Grid.Row="0" Grid.Column="5" Grid.ColumnSpan="3" Name="cmbSubCategory"
DataContext="{Binding ElementName=cmbCategoryName,Path=Items,Mode=OneWay}"
ItemsSource="{Binding Path=SubCategories, Mode=OneWay}"
DisplayMemberPath="SubCategoryName"
SelectedValuePath="Id" />
当cmbCategoryName选中的Category改变,cmbSubCategory的 SubCategory集合也会随之改变。
本文链接
ASP.NET MVC 3支持两大类型的验证:服务端和客户端脚本验证。本文先介绍服务端验证。在前文也介绍过,服务器端的验证是发生在模型绑定的时候,在DefaultModelBinder中有如下方法会触发验证:
internal void BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, object model) {// need to replace the property filter + model object and create an inner binding context
ModelBindingContext newBindingContext = CreateComplexElementalModelBindingContext(controllerContext, bindingContext, model);
// validation
if (OnModelUpdating(controllerContext, newBindingContext)) {
BindProperties(controllerContext, newBindingContext);
OnModelUpdated(controllerContext, newBindingContext);
}
其中,OnModelUpdated方法是真正触发验证逻辑的地方:
protected virtual void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext) {Dictionary<string, bool> startedValid = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
var res = ModelValidator.GetModelValidator(bindingContext.ModelMetadata, controllerContext).Validate(null);
foreach (ModelValidationResult validationResult in res) {
string subPropertyName = CreateSubPropertyName(bindingContext.ModelName, validationResult.MemberName);
if (!startedValid.ContainsKey(subPropertyName)) {
startedValid[subPropertyName] = bindingContext.ModelState.IsValidField(subPropertyName);
}
if (startedValid[subPropertyName]) {
bindingContext.ModelState.AddModelError(subPropertyName, validationResult.Message);
}
}
}
首先,它获得一个ModelValidator,这个ModelValidator是一个CompositeModelValidator,然后调用其Validate方法:
public override IEnumerable<ModelValidationResult> Validate(object container) {bool propertiesValid = true;
foreach (ModelMetadata propertyMetadata in Metadata.Properties) {
foreach (ModelValidator propertyValidator in propertyMetadata.GetValidators(ControllerContext)) {
foreach (ModelValidationResult propertyResult in propertyValidator.Validate(Metadata.Model)) {
propertiesValid = false;
yield return new ModelValidationResult {
MemberName = DefaultModelBinder.CreateSubPropertyName(propertyMetadata.PropertyName, propertyResult.MemberName),
Message = propertyResult.Message
};
}
}
}
if (propertiesValid) {
var typeValidators = Metadata.GetValidators(ControllerContext);
foreach (ModelValidator typeValidator in typeValidators)
{
foreach (ModelValidationResult typeResult in typeValidator.Validate(container)) {
yield return typeResult;
}
}
}
}
首先是查找model的property上的attribute,然后将其转换成一个IModelValidator对象,这里使用的是适配器模式,看下GetValidators方法,这是ModelMetaData的方法:
public virtual IEnumerable<