Поиск…


Синтаксис

  • public static ReturnType MyExtensionMethod (этот целевой объект TargetType)
  • public static ReturnType MyExtensionMethod (этот целевой объект TargetType, TArg1 arg1, ...)

параметры

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

замечания

Методы расширения - это синтаксический сахар, который позволяет статическим методам быть вызванными в экземплярах объектов, как если бы они были членами самого типа.

Для методов расширения требуется явный целевой объект. Вам нужно будет использовать this ключевое слово для доступа к методу из самого расширенного типа.

Методы расширений должны быть объявлены статическими и должны находиться в статическом классе.

Какое пространство имен?

Выбор пространства имен для вашего класса методов расширения - это компромисс между видимостью и возможностью обнаружения.

Наиболее часто упоминаемый вариант состоит в том, чтобы иметь собственное пространство имен для ваших методов расширения. Однако это будет связано с коммуникационными усилиями, чтобы пользователи вашего кода знали, что существуют методы расширения и где их искать.

Альтернативой является выбор пространства имен, чтобы разработчики открывали ваши методы расширения через Intellisense. Поэтому, если вы хотите расширить класс Foo , логично поставить методы расширения в том же пространстве имен, что и Foo .

Важно понимать, что ничто не мешает вам использовать «чужое» пространство имен : Таким образом, если вы хотите расширить IEnumerable , вы можете добавить свой метод расширения в пространство имен System.Linq .

Это не всегда хорошая идея. Например, в одном конкретном случае вам может понадобиться расширить общий тип (например, bool IsApproxEqualTo(this double value, double other) ), но не имеет, что «загрязняет» всю System . В этом случае предпочтительнее выбрать локальное, конкретное пространство имен.

Наконец, также можно поместить методы расширения в пространство имен вообще !

Хороший справочный вопрос: как вы управляете пространствами имен ваших методов расширения?

применимость

Следует проявлять осторожность при создании методов расширения, чтобы гарантировать, что они подходят для всех возможных входных данных и не только релевантны конкретным ситуациям. Например, можно расширить системные классы, такие как string , что делает ваш новый код доступным для любой строки. Если вашему коду необходимо выполнить логику домена для определенного формата в домене, метод расширения не будет подходящим, поскольку его присутствие будет путать вызывающих абонентов, работающих с другими строками в системе.

Следующий список содержит основные функции и свойства методов расширения

  1. Это должен быть статический метод.
  2. Он должен находиться в статическом классе.
  3. Он использует ключевое слово «this» в качестве первого параметра с типом в .NET, и этот метод будет вызываться экземпляром определенного типа на стороне клиента.
  4. Это также показано VS intellisense. Когда мы нажимаем точку . после экземпляра типа, то он приходит в VS intellisense.
  5. Метод расширения должен находиться в том же пространстве имен, в котором он используется, или вам нужно импортировать пространство имен класса с помощью инструкции using.
  6. Вы можете указать любое имя для класса с методом расширения, но класс должен быть статическим.
  7. Если вы хотите добавить новые методы к типу, и у вас нет исходного кода для него, тогда решение должно использовать и применять методы расширения этого типа.
  8. Если вы создаете методы расширения, которые имеют те же методы подписи, что и тип, который вы распространяете, методы расширения никогда не будут вызываться.

Методы расширения - обзор

Методы расширения были введены в C # 3.0. Методы расширения расширяют и добавляют поведение к существующим типам без создания нового производного типа, перекомпиляции или иного изменения исходного типа. Они особенно полезны, когда вы не можете изменить источник типа, который вы хотите улучшить. Методы расширения могут быть созданы для типов систем, типов, определенных третьими лицами, и типов, которые вы определили сами. Метод расширения может быть вызван, как если бы это был метод-член исходного типа. Это позволяет использовать метод Chaining для реализации Fluent Interface .

Метод расширения создается путем добавления статического метода к статическому классу, который отличается от исходного типа. Статический класс, содержащий метод расширения, часто создается с единственной целью хранения методов расширения.

В методах расширения используется специальный первый параметр, который указывает, что исходный тип будет расширен. Этот первый параметр украшен ключевым словом this (что представляет собой особое и четкое использование this в C # - это следует понимать как отличающееся от использования this которое позволяет ссылаться на членов экземпляра текущего объекта).

В следующем примере расширением исходного типа является string класса. String была расширена с помощью метода Shorten() , который обеспечивает дополнительную функциональность сокращения. Статический класс StringExtensions был создан для хранения метода расширения. Метод расширения Shorten() показывает, что это расширение string через специально выделенный первый параметр. Чтобы показать, что метод Shorten() расширяет string , первый параметр отмечен this . Таким образом, полная подпись первого параметра - this string text , где string является расширением исходного типа, а text - выбранным именем параметра.

static class StringExtensions
{
    public static string Shorten(this string text, int length) 
    {
        return text.Substring(0, length);
    }
}

class Program
{
    static void Main()
    {
        // This calls method String.ToUpper()
        var myString = "Hello World!".ToUpper();

        // This calls the extension method StringExtensions.Shorten()
        var newString = myString.Shorten(5); 

        // It is worth noting that the above call is purely syntactic sugar
        // and the assignment below is functionally equivalent
        var newString2 = StringExtensions.Shorten(myString, 5);
    }
}

Живая демонстрация на .NET скрипке


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

Например, когда этот код выполняется:

"some string".Shorten(5);

Значения аргументов таковы:

text: "some string"
length: 5

Обратите внимание, что методы расширения доступны только в том случае, если они находятся в том же пространстве имен, что и их определение, если пространство имен явно импортировано кодом с использованием метода расширения или если класс расширения меньше пространства имен. В директивах .NET Framework рекомендуется помещать классы расширения в собственное пространство имен. Однако это может привести к обнаружению проблем.

Это не приводит к конфликтам между методами расширения и используемыми библиотеками, если явно не затронуты пространства имен, которые могут конфликтовать. Например, расширения LINQ :

using System.Linq; // Allows use of extension methods from the System.Linq namespace

class Program
{
    static void Main()
    {
        var ints = new int[] {1, 2, 3, 4};

        // Call Where() extension method from the System.Linq namespace
        var even = ints.Where(x => x % 2 == 0); 
    }
}

Живая демонстрация на .NET скрипке


Начиная с C # 6.0, также можно поместить using static директиву в класс, содержащий методы расширения. Например, using static System.Linq.Enumerable; , Это делает методы расширения из этого конкретного класса доступными без привлечения других типов из одного и того же пространства имен в область видимости.


Когда метод класса с той же сигнатурой доступен, компилятор приостанавливает его по вызову метода расширения. Например:

class Test
{
   public void Hello()
   {
       Console.WriteLine("From Test");
   }
}

static class TestExtensions
{
    public static void Hello(this Test test)
    {
        Console.WriteLine("From extension method");
    }
}

class Program
{
    static void Main()
    {
        Test t = new Test();
        t.Hello(); // Prints "From Test"
    }
}

Живая демонстрация на .NET Fiddle


Обратите внимание: если есть две функции расширения с одной и той же сигнатурой, и одна из них находится в одном и том же пространстве имен, то это будет приоритетным. С другой стороны, если оба из них будут доступны с using , то с сообщением будет записана ошибка времени компиляции:

Вызов неоднозначен между следующими способами или свойствами


Обратите внимание, что синтаксическое удобство вызова метода расширения через originalTypeInstance.ExtensionMethod() является дополнительным удобством. Этот метод также можно вызывать традиционным способом, так что в качестве параметра к методу используется специальный первый параметр.

То есть, обе следующие работы:

//Calling as though method belongs to string--it seamlessly extends string
String s = "Hello World";
s.Shorten(5);  

//Calling as a traditional static method with two parameters
StringExtensions.Shorten(s, 5);

Явно использую метод расширения

Методы расширения можно также использовать как обычные методы статического класса. Этот способ вызова метода расширения более подробен, но необходим в некоторых случаях.

static class StringExtensions
{
    public static string Shorten(this string text, int length) 
    {
        return text.Substring(0, length);
    }
}

Использование:

var newString = StringExtensions.Shorten("Hello World", 5);

Когда вызывать методы расширения как статические методы

Есть еще сценарии, где вам нужно использовать метод расширения как статический метод:

  • Разрешение конфликта с методом участника. Это может произойти, если новая версия библиотеки представляет новый метод-член с той же сигнатурой. В этом случае метод-член будет предпочтительнее компилятором.
  • Разрешение конфликтов с другим методом расширения с одной и той же сигнатурой. Это может произойти, если две библиотеки включают аналогичные методы расширения, а пространства имен обоих классов с методами расширения используются в одном файле.
  • Передача метода расширения в качестве группы методов в параметр делегата.
  • Выполнение собственной привязки через Reflection .
  • Использование метода расширения в окне Immediate в Visual Studio.

Использование статических

Если using static директиву для переноса статических членов статического класса в глобальную область, методы расширения пропускаются. Пример:

using static OurNamespace.StringExtensions; // refers to class in previous example

// OK: extension method syntax still works.
"Hello World".Shorten(5);
// OK: static method syntax still works.
OurNamespace.StringExtensions.Shorten("Hello World", 5);
// Compile time error: extension methods can't be called as static without specifying class.
Shorten("Hello World", 5);

Если вы удалите this модификатор из первого аргумента метода Shorten , последняя строка будет скомпилирована.

Проверка нулей

Методы расширения - это статические методы, которые ведут себя как методы экземпляра. Однако, в отличие от того, что происходит при вызове метода экземпляра на null ссылке, когда метод расширения вызывается с null ссылкой, он не бросает NullReferenceException . Это может быть весьма полезно в некоторых сценариях.

Например, рассмотрим следующий статический класс:

public static class StringExtensions
{
    public static string EmptyIfNull(this string text)
    {
        return text ?? String.Empty;
    }

    public static string NullIfEmpty(this string text)
    {
        return String.Empty == text ? null : text;
    }
}
string nullString = null;
string emptyString = nullString.EmptyIfNull();// will return ""
string anotherNullString = emptyString.NullIfEmpty(); // will return null

Живая демонстрация на .NET скрипке

Методы расширения могут видеть только общедоступные (или внутренние) члены расширенного класса

public class SomeClass
{
    public void DoStuff()
    {
        
    }

    protected void DoMagic()
    {
        
    }
}

public static class SomeClassExtensions
{
    public static void DoStuffWrapper(this SomeClass someInstance)
    {
        someInstance.DoStuff(); // ok
    }

    public static void DoMagicWrapper(this SomeClass someInstance)
    {
        someInstance.DoMagic(); // compilation error
    }
}

Методы расширения - это просто синтаксический сахар и на самом деле не являются членами класса, который они расширяют. Это означает, что они не могут разрушить инкапсуляцию - они имеют доступ только к public (или реализованным в тех же сборках, internal ) полям, свойствам и методам.

Общие методы расширения

Как и другие методы, методы расширения могут использовать дженерики. Например:

static class Extensions
{
    public static bool HasMoreThanThreeElements<T>(this IEnumerable<T> enumerable)
    {
        return enumerable.Take(4).Count() > 3;
    }
}

и называть это было бы так:

IEnumerable<int> numbers = new List<int> {1,2,3,4,5,6};
var hasMoreThanThreeElements = numbers.HasMoreThanThreeElements();

Посмотреть демо

Аналогично для нескольких аргументов типа:

public static TU GenericExt<T, TU>(this T obj)
{
     TU ret = default(TU);
     // do some stuff with obj
     return ret;
}

Вызвать это было бы так:

IEnumerable<int> numbers = new List<int> {1,2,3,4,5,6};
var result = numbers.GenericExt<IEnumerable<int>,String>();

Посмотреть демо

Вы также можете создавать методы расширения для частично связанных типов в нескольких общих типах:

class MyType<T1, T2>
{
}

static class Extensions
{
    public static void Example<T>(this MyType<int, T> test)
    {        
    }
}

Вызвать это было бы так:

MyType<int, string> t = new MyType<int, string>();
t.Example();

Посмотреть демо

Кроме того, можно указать ограничения типа с , where :

public static bool IsDefault<T>(this T obj) where T : struct, IEquatable<T>
{
     return EqualityComparer<T>.Default.Equals(obj, default(T));
}

Код звонка:

int number = 5;
var IsDefault = number.IsDefault();

Посмотреть демо

Распространение методов расширения на основе статического типа

Для соответствия параметрам используется статический (время компиляции), а не динамический (тип времени выполнения).

public class Base 
{ 
    public virtual string GetName()
    {
        return "Base";
    }
}

public class Derived : Base
{ 
    public override string GetName()
    {
        return "Derived";
    }
}

public static class Extensions
{
    public static string GetNameByExtension(this Base item)
    {
        return "Base";
    }

    public static string GetNameByExtension(this Derived item)
    {
        return "Derived";
    }
}

public static class Program   
{
    public static void Main()
    {
        Derived derived = new Derived();
        Base @base = derived;

        // Use the instance method "GetName"
        Console.WriteLine(derived.GetName()); // Prints "Derived"
        Console.WriteLine(@base.GetName()); // Prints "Derived"

        // Use the static extension method "GetNameByExtension"
        Console.WriteLine(derived.GetNameByExtension()); // Prints "Derived"
        Console.WriteLine(@base.GetNameByExtension()); // Prints "Base"
    }
}

Живая демонстрация на .NET скрипке

Также отправка на основе статического типа не позволяет вызвать метод расширения для dynamic объекта:

public class Person
{
    public string Name { get; set; }
}

public static class ExtenionPerson
{
    public static string GetPersonName(this Person person)
    {
        return person.Name;
    }
}

dynamic person = new Person { Name = "Jon" };
var name = person.GetPersonName(); // RuntimeBinderException is thrown

Методы расширения не поддерживаются динамическим кодом.

static class Program
{
    static void Main()
    {
        dynamic dynamicObject = new ExpandoObject();

        string awesomeString = "Awesome";

        // Prints True
        Console.WriteLine(awesomeString.IsThisAwesome());

        dynamicObject.StringValue = awesomeString;

        // Prints True
        Console.WriteLine(StringExtensions.IsThisAwesome(dynamicObject.StringValue)); 
        
        // No compile time error or warning, but on runtime throws RuntimeBinderException
        Console.WriteLine(dynamicObject.StringValue.IsThisAwesome());
    }
}

static class StringExtensions
{
    public static bool IsThisAwesome(this string value)
    {
        return value.Equals("Awesome");
    }
}

Причина, по которой [вызов методов расширения из динамического кода] не работает, заключается в том, что в обычных, нединамических методах расширения кода работают путем полного поиска всех классов, известных компилятору для статического класса, который имеет метод расширения, который соответствует , Поиск выполняется в порядке, основанном на вложенности пространства имен и доступном using директив в каждом пространстве имен.

Это означает, что для того, чтобы вызов динамического метода расширения был разрешен правильно, DLL должна знать во время выполнения, что все вложенные пространства имен и using директивы были в вашем исходном коде . У нас нет механизма для кодирования всей этой информации на сайт вызова. Мы считали, что изобретаем такой механизм, но решили, что он слишком дорогостоящий, и он слишком много рискует, чтобы его стоило.

Источник

Методы расширения как строго типизированные обертки

Методы расширения можно использовать для написания сильно типизированных оболочек для словарных объектов. Например, кеш, HttpContext.Items в cetera ...

public static class CacheExtensions
{
    public static void SetUserInfo(this Cache cache, UserInfo data) => 
        cache["UserInfo"] = data;

    public static UserInfo GetUserInfo(this Cache cache) => 
        cache["UserInfo"] as UserInfo;
}

Этот подход устраняет необходимость использования строковых литералов в качестве ключей по всей кодовой базе, а также необходимость литья требуемого типа во время операции чтения. В целом он создает более безопасный, строго типизированный способ взаимодействия с такими свободно типизированными объектами, как словари.

Методы расширения для цепочки

Когда метод расширения возвращает значение , которое имеет тот же тип , как this аргумент, он может быть использован для «цепи» один или более вызовов метода совместимой подписи. Это может быть полезно для запечатанных и / или примитивных типов и позволяет создавать так называемые «свободные» API, если имена методов читаются как естественный человеческий язык.

void Main()
{
    int result = 5.Increment().Decrement().Increment(); 
    // result is now 6
}

public static class IntExtensions 
{
    public static int Increment(this int number) {
        return ++number;
    }

    public static int Decrement(this int number) {
        return --number;
    }
}

Или как это

void Main()
{
    int[] ints = new[] { 1, 2, 3, 4, 5, 6};
    int[] a = ints.WhereEven();
    //a is { 2, 4, 6 };
    int[] b = ints.WhereEven().WhereGreaterThan(2);
    //b is { 4, 6 };
}

public static class IntArrayExtensions
{
    public static int[] WhereEven(this int[] array)
    {
        //Enumerable.* extension methods use a fluent approach
        return array.Where(i => (i%2) == 0).ToArray();
    }

    public static int[] WhereGreaterThan(this int[] array, int value)
    {
        return array.Where(i => i > value).ToArray();
    }
}

Методы расширения в сочетании с интерфейсами

Очень удобно использовать методы расширения с интерфейсами, поскольку реализация может храниться вне класса, и все, что требуется для добавления некоторой функциональности в класс, - это украсить класс интерфейсом.

public interface IInterface
{
   string Do()
}

public static class ExtensionMethods{
    public static string DoWith(this IInterface obj){
      //does something with IInterface instance
    }
}

public class Classy : IInterface
{
   // this is a wrapper method; you could also call DoWith() on a Classy instance directly,
   // provided you import the namespace containing the extension method
   public Do(){
       return this.DoWith();
   }
}

используйте как:

 var classy = new Classy();
 classy.Do(); // will call the extension
 classy.DoWith(); // Classy implements IInterface so it can also be called this way

IList Пример метода расширения: сравнение 2 списков

Вы можете использовать следующий метод расширения для сравнения содержимого двух экземпляров IList <T> того же типа.

По умолчанию элементы сравниваются по их порядку в списке и сами элементы, передавая false параметру isOrdered будут сравнивать только сами элементы, независимо от их порядка.

Чтобы этот метод работал, общий тип ( T ) должен переопределять методы Equals и GetHashCode .

Использование:

List<string> list1 = new List<string> {"a1", "a2", null, "a3"};
List<string> list2 = new List<string> {"a1", "a2", "a3", null};

list1.Compare(list2);//this gives false
list1.Compare(list2, false);//this gives true. they are equal when the order is disregarded

Метод:

public static bool Compare<T>(this IList<T> list1, IList<T> list2, bool isOrdered = true) 
{
    if (list1 == null && list2 == null)
        return true;
    if (list1 == null || list2 == null || list1.Count != list2.Count)
        return false;

    if (isOrdered)
    {
        for (int i = 0; i < list2.Count; i++)
        {
            var l1 = list1[i]; 
            var l2 = list2[i];
            if (
                 (l1 == null && l2 != null) || 
                 (l1 != null && l2 == null) || 
                 (!l1.Equals(l2)))
            {
                    return false;
            }
        }
        return true;
    }
    else
    {
        List<T> list2Copy = new List<T>(list2);
        //Can be done with Dictionary without O(n^2)
        for (int i = 0; i < list1.Count; i++)
        {
            if (!list2Copy.Remove(list1[i]))
                return false;
        }
        return true;
    }
}

Методы расширения с перечислением

Методы расширения полезны для добавления функциональности в перечисления.

Одним из распространенных способов использования является метод преобразования.

public enum YesNo
{
    Yes,
    No,
}

public static class EnumExtentions
{
    public static bool ToBool(this YesNo yn)
    {
        return yn == YesNo.Yes;
    }
    public static YesNo ToYesNo(this bool yn)
    {
        return yn ? YesNo.Yes : YesNo.No;
    }
}

Теперь вы можете быстро преобразовать значение enum в другой тип. В этом случае bool.

bool yesNoBool = YesNo.Yes.ToBool(); // yesNoBool == true
YesNo yesNoEnum = false.ToYesNo();   // yesNoEnum == YesNo.No

Альтернативно, методы расширения могут использоваться для добавления свойств, подобных методам.

public enum Element
{
    Hydrogen,
    Helium,
    Lithium,
    Beryllium,
    Boron,
    Carbon,
    Nitrogen,
    Oxygen
    //Etc
}

public static class ElementExtensions
{
    public static double AtomicMass(this Element element)
    {
        switch(element)
        {
            case Element.Hydrogen:  return 1.00794;
            case Element.Helium:    return 4.002602;
            case Element.Lithium:   return 6.941;
            case Element.Beryllium: return 9.012182;
            case Element.Boron:     return 10.811;
            case Element.Carbon:    return 12.0107;
            case Element.Nitrogen:  return 14.0067;
            case Element.Oxygen:    return 15.9994;
            //Etc
        }
        return double.Nan;
    }
}

var massWater = 2*Element.Hydrogen.AtomicMass() + Element.Oxygen.AtomicMass();

Расширения и интерфейсы вместе позволяют DRY-код и смешанную функциональность

Методы расширения позволяют упростить определения вашего интерфейса, включая только основные необходимые функции в самом интерфейсе и позволяющие определять методы удобства и перегрузки как методы расширения. Интерфейсы с меньшим количеством методов легче реализовать в новых классах. Сохранение перегрузок в виде расширений, а не включение их в интерфейс напрямую избавляет вас от копирования кода шаблона в каждую реализацию, помогая вам сохранить код DRY. На самом деле это похоже на шаблон mixin, который C # не поддерживает.

Расширение System.Linq.Enumerable для IEnumerable<T> - отличный пример этого. IEnumerable<T> требует, чтобы реализующий класс реализовал два метода: общий и не общий GetEnumerator() . Но System.Linq.Enumerable предоставляет множество полезных утилит в качестве расширений, обеспечивающих краткое и четкое потребление IEnumerable<T> .

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

public interface ITimeFormatter
{
   string Format(TimeSpan span);
}

public static class TimeFormatter
{
    // Provide an overload to *all* implementers of ITimeFormatter.
    public static string Format(
        this ITimeFormatter formatter,
        int millisecondsSpan)
        => formatter.Format(TimeSpan.FromMilliseconds(millisecondsSpan));
}

// Implementations only need to provide one method. Very easy to
// write additional implementations.
public class SecondsTimeFormatter : ITimeFormatter
{
   public string Format(TimeSpan span)
   {
       return $"{(int)span.TotalSeconds}s";
   }
}

class Program
{
    static void Main(string[] args)
    {
        var formatter = new SecondsTimeFormatter();
        // Callers get two method overloads!
        Console.WriteLine($"4500ms is rougly {formatter.Format(4500)}");
        var span = TimeSpan.FromSeconds(5);
        Console.WriteLine($"{span} is formatted as {formatter.Format(span)}");
    }
}

Методы расширения для обработки особых случаев

Методы расширения могут использоваться для «скрытия» обработки неэлегантных бизнес-правил, которые в противном случае потребовали бы загромождения вызывающей функции с помощью операторов if / then. Это аналогично и аналогично обработке нулей с помощью методов расширения. Например,

public static class CakeExtensions
{
    public static Cake EnsureTrueCake(this Cake cake)
    {
        //If the cake is a lie, substitute a cake from grandma, whose cakes aren't as tasty but are known never to be lies. If the cake isn't a lie, don't do anything and return it.
        return CakeVerificationService.IsCakeLie(cake) ? GrandmasKitchen.Get1950sCake() : cake;
    }
}
Cake myCake = Bakery.GetNextCake().EnsureTrueCake();
myMouth.Eat(myCake);//Eat the cake, confident that it is not a lie.

Использование методов расширения со статическими методами и обратными вызовами

Рассмотрите возможность использования методов расширения как функций, которые обертывают другой код, вот отличный пример, который использует как метод статического метода, так и метод расширения, чтобы обернуть конструкцию Try Catch. Сделайте свой код Bullet Proof ...

using System;
using System.Diagnostics;

namespace Samples
{
    /// <summary>
    /// Wraps a try catch statement as a static helper which uses 
    /// Extension methods for the exception
    /// </summary>
    public static class Bullet
    {
        /// <summary>
        /// Wrapper for Try Catch Statement
        /// </summary>
        /// <param name="code">Call back for code</param>
        /// <param name="error">Already handled and logged exception</param>
        public static void Proof(Action code, Action<Exception> error)
        {
            try
            {
                code();
            }
            catch (Exception iox)
            {
                //extension method used here
                iox.Log("BP2200-ERR-Unexpected Error");
                //callback, exception already handled and logged
                error(iox);
            }
        }
        /// <summary>
        /// Example of a logging method helper, this is the extension method
        /// </summary>
        /// <param name="error">The Exception to log</param>
        /// <param name="messageID">A unique error ID header</param>
        public static void Log(this Exception error, string messageID)
        {
            Trace.WriteLine(messageID);
            Trace.WriteLine(error.Message);
            Trace.WriteLine(error.StackTrace);
            Trace.WriteLine("");
        }
    }
    /// <summary>
    /// Shows how to use both the wrapper and extension methods.
    /// </summary>
    public class UseBulletProofing
    {
        public UseBulletProofing()
        {
            var ok = false;
            var result = DoSomething();
            if (!result.Contains("ERR"))
            {
                ok = true;
                DoSomethingElse();
            }
        }

        /// <summary>
        /// How to use Bullet Proofing in your code.
        /// </summary>
        /// <returns>A string</returns>
        public string DoSomething()
        {
            string result = string.Empty;
            //Note that the Bullet.Proof method forces this construct.
            Bullet.Proof(() =>
            {
                //this is the code callback
                result = "DST5900-INF-No Exceptions in this code";
            }, error =>
            {
                //error is the already logged and handled exception
                //determine the base result
                result = "DTS6200-ERR-An exception happened look at console log";
                if (error.Message.Contains("SomeMarker"))
                {
                    //filter the result for Something within the exception message
                    result = "DST6500-ERR-Some marker was found in the exception";
                }
            });
            return result;
        }

        /// <summary>
        /// Next step in workflow
        /// </summary>
        public void DoSomethingElse()
        {
            //Only called if no exception was thrown before
        }
    }
}

Методы расширения на интерфейсах

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

public interface IVehicle
{
    int MilesDriven { get; set; }
}

public static class Extensions
{
    public static int FeetDriven(this IVehicle vehicle)
    {
        return vehicle.MilesDriven * 5028;
    }
}

В этом примере метод FeetDriven может использоваться на любом IVehicle . Эта логика в этом методе применима ко всем IVehicle , так что это можно сделать так, чтобы не было FeetDriven в определении IVehicle которое будет реализовано одинаково для всех детей.

Использование методов расширения для создания красивых классов сопоставления

Мы можем создать лучшие классы карт с методами расширения. Предположим, что у меня есть некоторые классы DTO, такие как

 public class UserDTO
 {
        public AddressDTO Address { get; set; }
 }

 public class AddressDTO
 {
        public string Name { get; set; }
 }

и мне нужно сопоставить соответствующие классы модели представления

public class UserViewModel
{
    public AddressViewModel Address { get; set; }
}

public class AddressViewModel
{
    public string Name { get; set; }
}

то я могу создать свой класс mapper, как показано ниже

public static class ViewModelMapper
{
      public static UserViewModel ToViewModel(this UserDTO user)
      {
            return user == null ?
                null :
                new UserViewModel()
                {
                    Address = user.Address.ToViewModel()
                    // Job = user.Job.ToViewModel(),
                    // Contact = user.Contact.ToViewModel() .. and so on
                };
      }

      public static AddressViewModel ToViewModel(this AddressDTO userAddr)
      {
            return userAddr == null ?
                null :
                new AddressViewModel()
                {
                    Name = userAddr.Name
                };
      }
}

Затем, наконец, я могу вызвать свой картограф, как показано ниже.

    UserDTO userDTOObj = new UserDTO() {
            Address = new AddressDTO() {
                Name = "Address of the user"
            }
        };

    UserViewModel user = userDTOObj.ToViewModel(); // My DTO mapped to Viewmodel

Красота здесь - все методы сопоставления имеют общее имя (ToViewModel), и мы можем использовать его несколькими способами

Использование методов расширения для создания новых типов коллекций (например, DictList)

Вы можете создавать методы расширения для улучшения удобства использования для вложенных коллекций, таких как Dictionary со значением List<T> .

Рассмотрим следующие методы расширения:

public static class DictListExtensions
{
    public static void Add<TKey, TValue, TCollection>(this Dictionary<TKey, TCollection> dict, TKey key, TValue value)
            where TCollection : ICollection<TValue>, new()
    {
        TCollection list;
        if (!dict.TryGetValue(key, out list))
        {
            list = new TCollection();
            dict.Add(key, list);
        }

        list.Add(value);
    }

    public static bool Remove<TKey, TValue, TCollection>(this Dictionary<TKey, TCollection> dict, TKey key, TValue value)
        where TCollection : ICollection<TValue>
    {
        TCollection list;
        if (!dict.TryGetValue(key, out list))
        {
            return false;
        }

        var ret = list.Remove(value);
        if (list.Count == 0)
        {
            dict.Remove(key);
        }
        return ret;
    }
}

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

var dictList = new Dictionary<string, List<int>>();

dictList.Add("example", 5);
dictList.Add("example", 10);
dictList.Add("example", 15);

Console.WriteLine(String.Join(", ", dictList["example"])); // 5, 10, 15

dictList.Remove("example", 5);
dictList.Remove("example", 10);

Console.WriteLine(String.Join(", ", dictList["example"])); // 15

dictList.Remove("example", 15);

Console.WriteLine(dictList.ContainsKey("example")); // False

Посмотреть демо



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow