Что означает ошибка «Метод расширения должен быть статическим»? - PullRequest
9 голосов
/ 23 мая 2011

У меня проблемы с этим классом, в частности с методом:

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

Ответы [ 8 ]

15 голосов
/ 23 мая 2011

Вы не пытаетесь расширить 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) Они действительно не являются членамиклассы или интерфейсы и не могут получить доступ ни к чему, кроме открытых полей / свойств / методов.

б) Они могут расширять любой класс.Без ограничения вы можете поместить вызов метода DoSomething(this ThatObject instance) в класс с именем ThatObject.Проблема в том, что вы не можете получить доступ ни к чему, кроме открытого интерфейса, так как это метод расширения.

Запутанные возможности были бы бесконечны;)

4 голосов
/ 23 мая 2011

Создание статического метода было правильным решением, чтобы сделать его методом расширения (факт, что метод расширения был правильным или нет, является еще одним спором!).

Теперь, поскольку ваш метод является статическим, он присоединен к классу , где он определен, а не к любому экземпляру этого класса. Поэтому ключевое слово this здесь не имеет смысла. this означает «текущий экземпляр класса»: он не существует по определению в статическом методе.

«Это», к которому вы должны обратиться, это просто ваш входной параметр source. Поместите source туда, где было this, и убедитесь, что ваш код скомпилируется с желаемым эффектом.

2 голосов
/ 23 мая 2011

Методы расширения могут быть определены только как статические методы в статическом классе - это означает, что вы не можете использовать какие-либо переменные экземпляра этого класса - единственной зависимостью метода расширения должен быть экземпляр передаваемого вами типа и static / compileзначения времени.

Обходной путь в вашем случае может состоять в том, чтобы определить Skip и Take как const (и применить соответствующее постоянное значение для них обоих), или, в качестве альтернативы, передать их в качестве параметров вашему методу расширения.

1 голос
/ 23 мая 2011
public static class OrderByCollection
{
    // that's extension method
    // it should be static
    public static IQueryable<T> ExecuteOrderBys<T>(this IQueryable<T> source)
    {
    }
}
1 голос
/ 23 мая 2011

Расширение должно быть определено в статическом классе

Попробуйте это:

public static class YourExtension
{
    public static IQueryable<T> ExecuteOrderBys<T>(this IQueryable<T> source)
    {
        // do something
    }
}
0 голосов
/ 23 мая 2011

Сначала преобразуйте класс вашей коллекции в универсальный класс, это необходимо для использования вывода типа для IQueryable из T в методе расширения:

public class OrderByCollection<T> 
{
    private List<T> Orderings = new List<T>();
    ...
}

Затем объявите новый статический класс, содержащийметод расширения.Поскольку метод будет статическим, вы не можете использовать этот квалификатор, но экземпляр передается в качестве аргумента источника:

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 голосов
/ 23 мая 2011

Вы не должны обращаться к членам this.xxx, вы должны обращаться к ним через переменную this .. В этом случае: источник После создания метода и класса, в котором он находится, статический.

0 голосов
/ 23 мая 2011

Измените его на

public static class OrderByCollection

и

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

Ключевой проблемой здесь является ключевое слово static, которое указывает, что у класса нет собственного состояния.и что для метода не требуется никакой информации о состоянии.

...