“扩展方法必须是静态的”这个错误是什么意思?

10

我对这个类有困扰,尤其是这个方法:

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);

嗨Tom,你能否发布ExecuteOrderBys的代码? - Apoorv
2
这是你刚刚询问的同一个问题:https://dev59.com/jG025IYBdhLWcg3wSj8q - 是的,虽然是不同的类,但本质上是相同的问题。 - Oded
1
@Oded:这里的问题在于非静态方法,而不是非静态类。顺便说一下,这两个编译器错误信息都很有启发性。 - abatishchev
@Oded @Monster @Abat 我已经包含了一些代码以说明该类应如何工作,我现在非常困惑,我只希望有一个简单的方法,我认为这与我的其他问题不同,如果不是的话,请原谅我的困扰。 - Tom Gullen
我不认为我想让它成为扩展方法,我真的对此感到困惑。我只想它成为一个正常的方法,接受一些数据并返回一些数据,但是返回类型让我感到困惑。我不理解这个。 - Tom Gullen
8个回答

15
看起来你并没有试图扩展 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关键字有点困惑(我正在从另一个类似的函数复制方法头),这是否意味着它正在被扩展? - Tom Gullen

4
将您的方法改为静态方法是将其变成扩展方法的正确做法(是否将其变成扩展方法的事实是另一个争论的话题!)。
现在,由于您的方法是静态的,它附加到定义它的类而不是该类的任何实例上。这就是为什么关键字“this”在此处毫无意义的原因。“this”表示“类的当前实例”:在静态方法中根据定义不存在。
您需要引用的“this”只是您的输入参数“source”。将“source”放在“this”的位置上,确保您的代码将以所需的效果编译。

2

扩展方法只能在静态类中定义为静态方法 - 这意味着您不能使用此类的任何实例变量 - 扩展方法的唯一依赖应该是您传入的类型实例和静态/编译时值。

在您的情况下,解决方法可能是将 SkipTake 定义为常量(并为两者应用适当的常量值),或者将它们作为参数传递给您的扩展方法。


1

扩展必须在静态类中定义

试一下这个:

public static class YourExtension
{
    public static IQueryable<T> ExecuteOrderBys<T>(this IQueryable<T> source)
    {
        // do something
    }
}

谢谢,我从错误中发现了这一点,但它会引发其他错误,所以我不确定如何修复它。 - Tom Gullen

1
public static class OrderByCollection
{
    // that's extension method
    // it should be static
    public static IQueryable<T> ExecuteOrderBys<T>(this IQueryable<T> source)
    {
    }
}

0

改为

public static class OrderByCollection

并且

public static IQueryable<T> ExecuteOrderBys<T>(this IQueryable<T> source)

这里的关键问题是 static 关键字,它表示该类没有自己的状态,并且该方法不需要任何状态信息。


0

首先,将您的集合类转换为泛型类,这是为了能够在扩展方法中使用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);
      ...
  } 
}

0

你不应该通过 this.xxx 访问成员,而应该通过 this. 变量访问它们。在这种情况下:在将方法和包含它的类设为静态之后。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接