Могу ли я использовать методы расширения и LINQ в .NET 2.0 или 3.0?

Когда я пытаюсь добавить метод расширения с использованием среды выполнения .NET 2.0 или 3.0, я получаю сообщение об ошибке:

Невозможно определить новый метод расширения, поскольку не найден требуемый компилятором тип System.Runtime.CompilerServices.ExtensionAttribute. Вам не хватает ссылки на System.Core.dll?

Но я не могу найти System.Core в списке доступных ссылок, когда пытаюсь добавить его в проект. Что мне нужно сделать, чтобы иметь возможность использовать методы расширения и, в свою очередь, LINQ в моих проектах ?


person Scott Chamberlain    schedule 05.07.2012    source источник


Ответы (1)


Методы расширения не добавлялись в .NET до версии 3.5. Однако это было не изменение CLR, а изменение компилятора, добавил их, так что вы все еще можете использовать их в своих проектах 2.0 и 3.0! Единственное требование - у вас должен быть компилятор, который может создавать проекты 3.5, чтобы иметь возможность делать это обходное решение (Visual Studio 2008 и выше).

Ошибка, которую вы получаете, когда пытаетесь использовать метод расширения, вводит в заблуждение, поскольку вам действительно не нужно System.Core.dll использовать методы расширения. Когда вы используете метод расширения, за кулисами компилятор добавляет _ 2_ функции. Если у вас есть компилятор, который понимает, что делать с атрибутом [Extension], вы можете использовать его в своих проектах 2.0 и 3.0, если создадите атрибут самостоятельно.

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

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
    public class ExtensionAttribute : Attribute
    {
    }
}

Приведенный выше блок кода находится внутри System.Core.Dll, поэтому в сообщении об ошибке говорится, что для их использования необходимо включить файл DLL.


Теперь, если вам нужна функциональность LINQ, это потребует немного дополнительной работы. Вам нужно будет заново реализовать методы расширения самостоятельно. Чтобы имитировать полную функциональность LINQ to SQL, код может стать довольно сложным. . Однако, если вы просто используете LINQ to Objects, большинство методов LINQ не сложно реализовать. Вот несколько функций замены LINQ to Objects из проекта, который я написал для начала.

public static class LinqReplacement
{
    public delegate TResult Func<T, TResult>(T arg);
    public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);

    public static TSource First<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        if (source == null)
            throw new ArgumentNullException("source");
        if (predicate == null)
            throw new ArgumentNullException("predicate");

        foreach (TSource item in source)
        {
            if (predicate(item) == true)
                return item;
        }

        throw new InvalidOperationException("No item satisfied the predicate or the source collection was empty.");
    }

    public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source)
    {
        if (source == null)
            throw new ArgumentNullException("source");

        foreach (TSource item in source)
        {
            return item;
        }

        return default(TSource);
    }

    public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
    {
        foreach (object item in source)
        {
            yield return (TResult)item;
        }
    }

    public static IEnumerable<TResult> SelectMany<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
    {
        if (source == null)
            throw new ArgumentNullException("source");
        if (selector == null)
            throw new ArgumentNullException("selector");

        foreach (TSource item in source)
        {
            foreach (TResult subItem in selector(item))
            {
                yield return subItem;
            }
        }
    }

    public static int Count<TSource>(this IEnumerable<TSource> source)
    {
        var asCollection = source as ICollection;
        if(asCollection != null)
        {
            return asCollection.Count;
        }

        int count = 0;
        foreach (TSource item in source)
        {
            checked //If we are counting a larger than int.MaxValue enumerable this will cause a OverflowException to happen when the counter wraps around.
            {
                count++;
            }
        }
        return count;
    }
}

Библиотеку с полной повторной реализацией LINQ to Objects с уже добавленным ExtensionAttribute можно найти в Проект LinqBridge (спасибо, Аллону Гуралнеку).

person Scott Chamberlain    schedule 05.07.2012
comment
Важно отметить, что ваши методы LinqReplacement будут работать только с Linq to Objects. Это не будет работать с Linq to Sql. Кажется, что многие люди не понимают, что есть разница. Но все же +1 - person cadrell0; 05.07.2012
comment
Вам не нужно заново реализовывать методы расширения самостоятельно. Полный провайдер LINQ-to-Objects уже давно повторно реализован для .NET 2.0 как LinqBridge. И он уже включает ExtensionAttribute, который позволяет создавать методы расширения в .NET 2.0 с VS 2008 и выше. - person Allon Guralnek; 05.07.2012
comment
@AllonGuralnek Спасибо за ссылку, обновил ответ и дал вам кредит. - person Scott Chamberlain; 05.07.2012
comment
@ cadrell0 отчасти верно, вы все равно можете использовать его с вызовами ADO.NET, которые делают while(reader.Read()) yield BuildObject(reader). Конечно, он будет делать все в памяти, как если бы вы расставили .AsEnumerable() точки повсюду, и он будет читать столбцы, которые ему не нужны, но такой подход все еще был полезен в дни до Linq. - person Jon Hanna; 04.06.2014