我在使用这个类时遇到了问题,特别是方法:
public IQueryable<T> ExecuteOrderBys<T>(this IQueryable<T> source)
它说错误:
扩展方法必须是静态的
但是,当我使该方法成为静态时,它会抛出其他区域,特别是this.xxx
无法在静态方法中访问的区域。
我对带有<T>
的返回类型和返回类型有点困惑,如果有人可以向我解释它以及它是如何工作的,我将不胜感激。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Collections;
/// <summary>
/// A collection of order bys
/// </summary>
public class OrderByCollection
{
private ArrayList Orderings = new ArrayList();
public int? Skip { get; set; }
public int? Take { get; set; }
public OrderByCollection()
{
// Default skip and takes to nulls so we know if they are set or not
this.Skip = null;
this.Take = null;
}
/// <summary>
/// Add an order by to this collection
/// </summary>
public void AddOrderBy(string Field, bool Descending)
{
OrderByObj NewObj = new OrderByObj(Descending, Field);
this.Orderings.Add(NewObj);
}
/// <summary>
/// Executes the order bys
/// </summary>
public IQueryable<T> ExecuteOrderBys<T>(this IQueryable<T> source)
{
int ExecutionIndex = 0;
foreach (OrderByObj O in this.Orderings)
{
if (ExecutionIndex == 0)
{
if (O.Descending)
source = LinqHelper.OrderByDescending(source, O.Field);
else
source = LinqHelper.OrderBy(source, O.Field);
}
else
{
if (O.Descending)
source = LinqHelper.ThenByDescending((IOrderedQueryable<T>)source, O.Field);
else
source = LinqHelper.ThenBy((IOrderedQueryable<T>)source, O.Field);
}
ExecutionIndex++;
}
// Skip and take
if (this.Skip != null)
source = source.Skip(this.Skip.Value);
if (this.Take != null)
source = source.Take(this.Take.Value);
return (IOrderedQueryable<T>)source;
}
}
编辑
我正在尝试创建一个可以执行以下操作的类:
var q = db.tblJobHeaders;
OrderByCollection OBys = new OrderByCollection();
OBys.AddOrderBy("some field", true);
OBys.AddOrderBy("anotherfield", false);
OBys.ExecuteOrderBys(q);
您根本不试图通过示例的外观来扩展IQueryable
。从方法定义中删除this
,您的示例应该可以正常工作。
public class OrderByCollection
{
// .. shameless cut ..
public IQueryable<T> ExecuteOrderBys<T>(IQueryable<T> source)
{
// .. we don't need no stinking body ..
}
}
这将使您的示例工作:
var q = db.tblJobHeaders;
OrderByCollection OBys = new OrderByCollection();
OBys.AddOrderBy("some field", true);
OBys.AddOrderBy("anotherfield", false);
OBys.ExecuteOrderBys(q);
作为旁注,我不会在bool
来定义排序方式。代码不清楚。请改用枚举或其他方法:
OBys.AddOrderBy("some field", Sort.Ascending);
或:
OBys.AddOrderByDescending("some field");
更新
扩展方法用于将方法"插件"到现有类或接口。通过写public IQueryable<T> ExecuteOrderBys<T>(this IQueryable<T> source)
你实际上是在说该方法应该挂接到IQueryable<T>
。因此,应该像myQuery.ExecuteOrderBys
一样访问它。
我的猜测是扩展方法必须是静态的,并且包含在静态类中以避免混淆:
a) 它们实际上不是类或接口的成员,除了公共字段/属性/方法之外,无法访问任何其他内容。
b) 它们可以扩展任何类。如果没有限制,您可以将方法调用DoSomething(this ThatObject instance)
放在名为 ThatObject
的类中。问题是除了公共接口之外,您无法访问任何其他内容,因为它是一种扩展方法。
令人困惑的可能性将是无穷无尽的;)
使方法静态是使其成为扩展方法的正确做法(使扩展方法正确与否是另一个争论!
现在,由于您的方法是静态的,因此它附加到定义它的类,而不是此类的任何实例。这就是为什么关键字this
在这里毫无意义。 this
的意思是"类的当前实例":根据定义,它在静态方法中不存在。
您需要引用的"this"只是您的输入参数source
。把source
放在有this
的地方,并确保你的代码将以预期的效果进行编译。
扩展方法只能定义为静态类中的静态方法 - 这意味着您不能使用此类的任何实例变量 - 扩展方法的唯一依赖项应该是您传入的类型的实例和静态/编译时值。
在您的情况下,解决方法可能是将 Skip
和 Take
定义为 const(并为它们应用适当的常量值),或者将它们作为参数传递给扩展方法。
必须在静态类中定义
试试这个:
public static class YourExtension
{
public static IQueryable<T> ExecuteOrderBys<T>(this IQueryable<T> source)
{
// do something
}
}
public static class OrderByCollection
{
// that's extension method
// it should be static
public static IQueryable<T> ExecuteOrderBys<T>(this IQueryable<T> source)
{
}
}
将其更改为
public static class OrderByCollection
和
public static IQueryable<T> ExecuteOrderBys<T>(this IQueryable<T> source)
这里的关键问题是 static
关键字,它表示类没有任何自己的状态,并且该方法不需要任何状态信息。
你不应该通过 this.xxx 访问成员,你应该通过 this.. 变量访问它们。在这种情况下:来源在使方法和包含它的类成为静态之后。
首先,将你的集合类转换为泛型类,你需要这个才能在扩展方法中使用 T 的 IQueryable 的类型推断:
public class OrderByCollection<T>
{
private List<T> Orderings = new List<T>();
...
}
然后声明一个包含扩展方法的新静态类。由于该方法将是静态的,因此不能使用 this 限定符,但作为源参数传递的实例:
public static class Extensions {
public static IQueryable<T> ExecuteOrderBys<T>(this OrderByCollection<T> source) {
// instead of this.xxx use source.xxx
IQueryable<T> result;
...
if (source.Skip != null)
result = source.Skip(this.Skip.Value);
if (source.Take != null)
result = source.Take(this.Take.Value);
...
}
}