Есть ли в C # свойства расширения?
Например, я могу добавить свойство расширения к DateTimeFormatInfo
вызываемому, ShortDateLongTimeFormat
который будет возвращать ShortDatePattern + " " + LongTimePattern
?
На данный момент он все еще не поддерживается из коробки компилятором Roslyn ...
До сих пор свойства расширения не рассматривались как достаточно ценные, чтобы их можно было включить в предыдущие версии стандарта C #. C # 7 и C # 8.0 считают это чемпионом предложения, но оно еще не было выпущено, прежде всего потому, что, даже если уже есть реализация, они хотят сделать это с самого начала.
Но это будет ...
В рабочем списке C # 7 есть элемент расширения, поэтому он может быть поддержан в ближайшем будущем. Текущий статус свойства extension можно найти на Github под соответствующим элементом .
Тем не менее, есть еще более многообещающая тема: «Расширить все» с акцентом на свойствах, статических классах или даже полях.
Кроме того, вы можете использовать обходной путь
Как указано в этой статье , вы можете использовать TypeDescriptor
возможность прикрепить атрибут к экземпляру объекта во время выполнения. Однако он не использует синтаксис стандартных свойств.
Это немного отличается от простого синтаксического сахара, добавляющего возможность определять расширенное свойство, например, string Data(this MyClass instance)
в качестве псевдонима для метода расширения, так string GetData(this MyClass instance)
как оно хранит данные в классе.
Я надеюсь, что C # 7 обеспечит полнофункциональное расширение всего (свойств и полей), однако на данный момент только время покажет.
И не стесняйтесь вносить свой вклад, поскольку программное обеспечение завтрашнего дня придет от сообщества.
Обновление: август 2016
Как команда dotnet опубликовала, что нового в C # 7.0 и из комментария Мадса Торгенсена :
Свойства расширения: у нас (блестящий!) Молодой специалист реализовал их летом в качестве эксперимента вместе с другими типами членов расширения. Мы по-прежнему заинтересованы в этом, но это большое изменение, и мы должны быть уверены, что оно того стоит.
Кажется, что свойства расширения и другие члены все еще являются хорошими кандидатами для включения в будущий выпуск Roslyn, но, возможно, не в 7.0.
Обновление: май 2017
Расширение членов было закрыто как дубликат расширения все проблемы, которые тоже закрыты. Основное обсуждение было на самом деле о расширяемости типов в широком смысле. Эта функция теперь отслеживается здесь как предложение и удалена из этапа 7.0 .
Обновление: август 2017 г. - C # 8.0 предлагаемая функция
Хотя это все еще остается только предложенной функцией, теперь у нас есть более четкое представление о том, каким будет ее синтаксис. Имейте в виду, что это будет новый синтаксис и для методов расширения:
public interface IEmployee
{
public decimal Salary { get; set; }
}
public class Employee
{
public decimal Salary { get; set; }
}
public extension MyPersonExtension extends Person : IEmployee
{
private static readonly ConditionalWeakTable<Person, Employee> _employees =
new ConditionalWeakTable<Person, Employee>();
public decimal Salary
{
get
{
// `this` is the instance of Person
return _employees.GetOrCreate(this).Salary;
}
set
{
Employee employee = null;
if (!_employees.TryGetValue(this, out employee)
{
employee = _employees.GetOrCreate(this);
}
employee.Salary = value;
}
}
}
IEmployee person = new Person();
var salary = person.Salary;
Подобно частичным классам, но скомпилирован как отдельный класс / тип в другой сборке. Обратите внимание, что вы также сможете добавлять статические члены и операторы таким образом. Как упоминалось в подкасте Mads Torgensen , расширение не будет иметь никакого состояния (поэтому оно не может добавлять частные элементы экземпляра в класс), что означает, что вы не сможете добавлять частные данные экземпляра, связанные с экземпляром . Причиной этого является то, что это подразумевает управление внутренними словарями, и это может быть затруднительно (управление памятью и т. Д.). Для этого вы все еще можете использовать метод TypeDescriptor
/ ConditionalWeakTable
, описанный ранее, и с расширением свойства скрывает его под хорошим свойством.
Синтаксис все еще может быть изменен, поскольку подразумевает эту проблему . Например, extends
может быть заменено тем, for
что некоторые могут чувствовать себя более естественно и менее связаны с Java.
Обновление декабря 2018 года - роли, расширения и статические члены интерфейса
Расширение все не дошло до C # 8.0, из-за некоторых недостатков, объясненных как конец этого билета GitHub . Итак, было проведено исследование для улучшения дизайна. Здесь Мадс Торгенсен объясняет, что такое роли и расширения и чем они отличаются:
Роли позволяют реализовывать интерфейсы для определенных значений данного типа. Расширения позволяют реализовывать интерфейсы для всех значений данного типа в пределах определенной области кода.
Это можно увидеть при разделении предыдущего предложения в двух случаях использования. Новый синтаксис для расширения будет выглядеть так:
public extension ULongEnumerable of ulong
{
public IEnumerator<byte> GetEnumerator()
{
for (int i = sizeof(ulong); i > 0; i--)
{
yield return unchecked((byte)(this >> (i-1)*8));
}
}
}
тогда вы сможете сделать это:
foreach (byte b in 0x_3A_9E_F1_C5_DA_F7_30_16ul)
{
WriteLine($"{e.Current:X}");
}
И для статического интерфейса :
public interface IMonoid<T> where T : IMonoid<T>
{
static T operator +(T t1, T t2);
static T Zero { get; }
}
Добавить свойство расширения на int
и лечить , int
как IMonoid<int>
:
public extension IntMonoid of int : IMonoid<int>
{
public static int Zero => 0;
}
Нет, они не существуют в C # 3.0 и не будут добавлены в 4.0. Он находится в списке желаний для C #, поэтому он может быть добавлен в будущем.
На данный момент лучшее, что вы можете сделать, - это методы расширения стиля GetXXX.
Нет, их не существует.
Я знаю, что команда C # рассматривала их в какой-то момент (или, по крайней мере, Эрик Липперт) - вместе с конструкторами и операторами расширений (это может занять некоторое время, чтобы разобраться, но это круто ...) Однако, у меня нет не видел никаких доказательств того, что они будут частью C # 4.
РЕДАКТИРОВАТЬ: они не появились в C # 5, и по состоянию на июль 2014 года, похоже, что это будет в C # 6 тоже.
Эрик Липперт , главный разработчик команды компилятора C # в Microsoft до ноября 2012 года, писал об этом в октябре 2009 года:
()
в конце гораздо более читабельно. Я лично знаю, что по крайней мере 90% написанных мной расширений относятся к этим двум типам. FullName
= FirstName + LastName
, ShortName
= FirstName + LastName[0]
. Я хотел бы добавить больше этих свойств, но я не хочу «портить» реальные классы. В этом случае свойство расширения, предназначенное только для чтения, идеально подходит, потому что я могу добавить функциональность, сохранить основной класс в чистоте и по-прежнему предоставлять информацию, которую я хочу предоставить в пользовательском интерфейсе. static implicit operator FileInfo(FileInfoEx fex)
который возвращает мой содержащийся объект FileInfo. Это позволяет мне обрабатывать FileInfoEx так, как если бы он наследовал от FileInfo, даже если этот класс запечатан. Поскольку я недавно нуждался в этом, я посмотрел на источник ответа в:
C # расширить класс, добавив свойства
и создал более динамическую версию:
public static class ObjectExtenders
{
static readonly ConditionalWeakTable<object, List<stringObject>> Flags = new ConditionalWeakTable<object, List<stringObject>>();
public static string GetFlags(this object objectItem, string key)
{
return Flags.GetOrCreateValue(objectItem).Single(x => x.Key == key).Value;
}
public static void SetFlags(this object objectItem, string key, string value)
{
if (Flags.GetOrCreateValue(objectItem).Any(x => x.Key == key))
{
Flags.GetOrCreateValue(objectItem).Single(x => x.Key == key).Value = value;
}
else
{
Flags.GetOrCreateValue(objectItem).Add(new stringObject()
{
Key = key,
Value = value
});
}
}
class stringObject
{
public string Key;
public string Value;
}
}
Вероятно, это может быть значительно улучшено (наименование, динамическое, а не строковое), в настоящее время я использую это в CF 3.5 вместе с хакерским ConditionalWeakTable ( https://gist.github.com/Jan-WillemdeBruyn/db79dd6fdef7b9845e217958db98c4d4 )
Как упоминалось @Psyonity, вы можете использовать conditionalWeakTable для добавления свойств к существующим объектам. В сочетании с динамическим ExpandoObject вы можете реализовать свойства динамического расширения в несколько строк:
using System.Dynamic;
using System.Runtime.CompilerServices;
namespace ExtensionProperties
{
/// <summary>
/// Dynamically associates properies to a random object instance
/// </summary>
/// <example>
/// var jan = new Person("Jan");
///
/// jan.Age = 24; // regular property of the person object;
/// jan.DynamicProperties().NumberOfDrinkingBuddies = 27; // not originally scoped to the person object;
///
/// if (jan.Age < jan.DynamicProperties().NumberOfDrinkingBuddies)
/// Console.WriteLine("Jan drinks too much");
/// </example>
/// <remarks>
/// If you get 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create' you should reference Microsoft.CSharp
/// </remarks>
public static class ObjectExtensions
{
///<summary>Stores extended data for objects</summary>
private static ConditionalWeakTable<object, object> extendedData = new ConditionalWeakTable<object, object>();
/// <summary>
/// Gets a dynamic collection of properties associated with an object instance,
/// with a lifetime scoped to the lifetime of the object
/// </summary>
/// <param name="obj">The object the properties are associated with</param>
/// <returns>A dynamic collection of properties associated with an object instance.</returns>
public static dynamic DynamicProperties(this object obj) => extendedData.GetValue(obj, _ => new ExpandoObject());
}
}
Пример использования в комментариях xml:
var jan = new Person("Jan");
jan.Age = 24; // regular property of the person object;
jan.DynamicProperties().NumberOfDrinkingBuddies = 27; // not originally scoped to the person object;
if (jan.Age < jan.DynamicProperties().NumberOfDrinkingBuddies)
{
Console.WriteLine("Jan drinks too much");
}
jan = null; // NumberOfDrinkingBuddies will also be erased during garbage collection
Обновление (спасибо @chaost за указание на это обновление):
Мэдс Торгерсен: «Расширение - все, что не вошло в C # 8.0. Он попал в« захватывающую »дискуссию о дальнейшем будущем языка, и теперь мы хотим убедиться, что мы этого не делаем». добавьте его таким образом, чтобы помешать этим будущим возможностям. Иногда языковой дизайн - очень длинная игра! »
Источник: раздел комментариев в https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/
Я перестал считать, сколько раз за эти годы я открыл этот вопрос в надежде увидеть, как это будет реализовано.
Ну наконец-то мы все можем порадоваться! Microsoft собирается представить это в своем следующем выпуске C # 8.
Так что вместо этого ...
public static class IntExtensions
{
public static bool Even(this int value)
{
return value % 2 == 0;
}
}
Мы наконец сможем сделать это так ...
public extension IntExtension extends int
{
public bool Even => this % 2 == 0;
}
Источник: https://blog.ndepend.com/c-8-0-features-glimpse-future/
?
enum
которые могут иметь свойства и методы. В C #enum
не может быть свойств или методов, но вы можете создавать методы расширения для них. Этот вопрос был полезен для меня и не должен быть закрыт.