C #에는 확장 속성이 있습니까?


768

C #에는 확장 속성이 있습니까?

예를 들어, DateTimeFormatInfo호출 ShortDateLongTimeFormat할 확장 속성을 추가 할 수 ShortDatePattern + " " + LongTimePattern있습니까?


14
Nullable <T>에 IsNull이라는 확장 메서드를 추가하고 싶었습니다. HasValue. .IsNull ()은 확실히 .IsNull보다 작습니다
Ken

1
나는 이것이 ?
삼항

2
나는 이것이 enum속성과 메소드를 가질 수있는 Java를 모방하기를 원했다 . C # enum은 속성이나 메서드를 가질 수 없지만 확장 메서드를 만들 있습니다. 이 질문은 나에게 도움이되었으므로 닫혀서는 안됩니다.
Ian McLaird

많은 사람들이 말했듯이 현재 언어에 이것을 추가 할 계획이 없지만 그렇게 할 수 없었습니다. F #에는 확장 속성뿐만 아니라 정적 확장 기능도 있다는 것이 적어도 좋은 아이디어라는 것을 증명합니다.
Richiban

2
이 이루어져야 하나
Rootel

답변:


365

현재 Roslyn 컴파일러는 여전히 기본적으로 지원하지 않습니다 ...

지금까지 확장 속성은 이전 버전의 C # 표준에 포함될만큼 가치있는 것으로 보이지 않았습니다. C # 7C # 8.0 은 이것을 제안 챔피언으로 보았지만 아직 구현되지 않았지만, 이미 구현이 있더라도 처음부터 올바르게 만들고 싶어하기 때문에 아직 출시되지 않았습니다.

그러나 그것은 ...

확장 회원 에서 항목 C # 7 작업 목록 이 가까운 미래에 지원 될 수 있도록. 확장 속성의 현재 상태는 Github의 관련 항목에서 찾을 수 있습니다 .

그러나 특히 속성과 정적 클래스 또는 필드에 중점을 둔 "모든 것을 확장" 하는 더 유망한 주제가 있습니다.

또한 해결 방법을 사용할 수 있습니다

기사 에서 지정한대로 , TypeDescriptor런타임시이 기능을 사용하여 객체 인스턴스에 속성을 첨부 할 수 있습니다 . 그러나 표준 속성의 구문을 사용하지 않습니다. 데이터가 클래스에 저장 될 때 확장 메소드의 별명
과 같은 확장 특성을 정의 할 수있는 가능성을 추가하는 구문 설탕과는 조금 다릅니다 .
string Data(this MyClass instance)
string GetData(this MyClass instance)

C # 7이 모든 기능 확장 (속성 및 필드)을 제공하기를 희망하지만 그 시점에서는 시간 만 알 수 있습니다.

또한 내일의 소프트웨어가 커뮤니티에서 제공 될 것이므로 자유롭게 기여하십시오.

업데이트 : 2016 년 8 월

닷넷 팀 은 C # 7.0의 새로운 기능Mads Torgensen 의 의견을 게시했습니다 .

확장 속성 : 우리는 다른 종류의 확장 멤버와 함께 여름에 실험을 통해 (화려한!) 인턴을 구현했습니다. 우리는 이것에 관심을 가지고 있지만 큰 변화이며 그만한 가치가 있다고 확신해야합니다.

확장 속성과 다른 멤버는 여전히 Roslyn의 향후 릴리스에 포함될 좋은 후보이지만 7.0은 아닙니다.

업데이트 : 2017 년 5 월

확장 멤버 확장 된 모든 문제 와 중복되어닫혔습니다. 주요 논의는 실제로 유형 확장성에 대한 광범위한 의미에서 논의되었습니다. 이 기능은 이제 제안서로 추적되었으며 7.0 마일스톤 에서 제거되었습니다.

업데이트 : 2017 년 8 월-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.

2018 년 12 월 업데이트-역할, 확장 및 정적 인터페이스 멤버

확장은 모든 GitHub 티켓 의 끝으로 설명 된 몇 가지 단점 때문에 C # 8.0으로 만들지 못했습니다 . 따라서 디자인을 개선하기위한 탐구가있었습니다. 여기서 , MADS Torgensen는 무엇인지 설명 역할과 확장 그리고 그들은 어떻게 다른지 :

역할을 통해 특정 유형의 특정 값에 인터페이스를 구현할 수 있습니다. 확장 기능을 사용하면 특정 코드 영역 내에서 지정된 유형의 모든 값에 인터페이스를 구현할 수 있습니다.

두 가지 사용 사례에서 이전 제안의 일부에서 볼 수 있습니다. 확장을위한 새로운 구문은 다음과 같이 될 것이다 :

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와 치료 intIMonoid<int>:

public extension IntMonoid of int : IMonoid<int>
{
    public static int Zero => 0;
}

57
이것은 내가 StackExchange에서 수행 한 가장 유용한 답변 중 하나입니다. 지속적으로 상태를 업데이트하고 모든 사람들에게 정보를 제공하여 토론과 역사에 대한 견고한 링크를 제공합니다.
bdrelling

25
감사합니다
David Thielen

1
불행하게도이 댓글의로, 역할, 확장 및 정적 인터페이스 멤버는 :( C # 11 플래그가
이안 켐프

436

C # 3.0에는 존재하지 않으며 4.0에는 추가되지 않습니다. C #에 필요한 기능 목록에 있으므로 향후 추가 될 수 있습니다.

이 시점에서 가장 좋은 방법은 GetXXX 스타일 확장 방법입니다.


3
일반 속성과 마찬가지로 'GetXXX <>'구문을 사용해야합니다.
Jay Bazuzi 2016 년

3
좋아, 그게 내가 생각한 것입니다. @Jay, 그래, 나도 싫어. 특히 일반 인덱서를 가질 수없는 ... sigh
Svish

75
원하는 기능 목록에 링크 하시겠습니까?
Dan Esparza

2
버전 6.0 및 7.0은 어떻습니까?
Falk

2
2020 년 기준으로이 업데이트가 있습니까?
차드

265

아니요, 존재하지 않습니다.

나는 C # 팀이 확장 생성자와 연산자와 함께 한 시점에서 (또는 적어도 Eric Lippert가) 고려하고 있음을 알고 있습니다. 그들이 C # 4의 일부가 될 것이라는 어떠한 증거도 보지 못했습니다.


편집 : 그들은 C # 5에 나타나지 않았으며 2014 년 7 월 현재 C # 6에도없는 것처럼 보이지 않습니다.

2012 년 11 월부터 Microsoft C # 컴파일러 팀의 수석 개발자 인 Eric Lippert 는 2009 년 10 월에 이에 대해 블로그를 작성했습니다.


2
예, 필드를 숨길 수 있습니다. 단일 속성을 설정하면 아래에 두 개의 속성이 설정되거나 그 반대로 설정 될 수 있습니다. (일반적인 Size 속성과 Width / Height 확장 속성을 가진 것을 상상해보십시오. 그 반대도 마찬가지입니다.)하지만 읽기 전용으로 더 유용 할 것입니다.
Jon Skeet

23
확장 메서드에는 바인딩 할 수 없습니다. 데이터 바인딩을위한 고유 한 속성을 추가 할 수 있으면 많은 상황에서 도움이 될 수 있습니다.
Nick

3
@leppie-속성 확장의 가치는 내가 생각하는 bool 및 string 속성에 가장 유리합니다. ()끝에를 제거하는 것이 훨씬 더 읽기 쉽습니다. 필자는 개인적으로 필자가 작성한 확장의 90 % 이상이 2 가지 유형임을 알고 있습니다.
Code Maverick

4
이것이 왜 유용한 지에 대한 예를 제공하기 위해 EFCF 모델이 있습니다. 일부 클래스에는 형식화 된 정보를 반환하는 데 사용하는 읽기 전용 속성이 있습니다 : FullName= FirstName + LastName, ShortName= FirstName + LastName[0]. 이러한 속성을 더 추가하고 싶지만 실제 클래스를 "더러워"싶지 않습니다. 이 경우 확장 기능은 읽기 전용이며 기능을 추가하고 기본 클래스를 깨끗하게 유지하면서 UI에 노출하려는 정보를 계속 노출 할 수 있기 때문에 완벽합니다.
Gup3rSuR4c 2019

4
@ JonSkeet : 맞습니다. 내 클래스를 만든 다음 관련 봉인 클래스 메소드와 속성을 모두 래핑하여 static implicit operator FileInfo(FileInfoEx fex)포함 된 FileInfo 객체를 반환 하여 원하는 것을 수행했습니다. 이렇게하면 클래스가 봉인되어 있어도 FileInfoEx가 FileInfo에서 상속 된 것처럼 효과적으로 처리 할 수 ​​있습니다.
Steve L

27

업데이트 ( 이 업데이트를 지적한 @chaost 덕분에 ) :

Mads Torgersen : "확장 된 모든 것이 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/


3
이번 주 C # 8.0 기능 이 발표되었으며 불행히도이 기능 을 보지 못했습니다.
Mateo Torres-Ruiz 21

1
@ MateoTorres-Ruiz 'Mads Torgersen'(C # dev)의 의견에 대해 누군가에게 묻습니다 (3 일 전). "확장 모든 것이 C # 8.0에 포함되지 않았습니다. , 언어의 미래에 대한 매우 흥미로운 토론에서, 우리는 미래의 가능성을 방해하는 방식으로 언어를 추가하지 않기를 원합니다. 언어 디자인은 매우 긴 게임입니다! " 나쁜 느낌 .. (Korayems 링크이 읽기, 코멘트 섹션에서)
Chaost

8

@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 &lt; 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

가장 좋은 답변
N73k

1

최근에 이것이 필요했기 때문에 답의 출처를 살펴 보았습니다.

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

아마 많이 향상 될 수 있습니다 (이름 대신, 문자열 대신 동적), 나는 현재 해킹 된 ConditionalWeakTable과 함께 CF 3.5에서 이것을 사용합니다 ( https://gist.github.com/Jan-WillemdeBruyn/db79dd6fdef7b9845e217958db98c4d4 )


죄송하지만, 이것은 매우 철저 해 보이지만 확장 속성과는 관련이 없지만 확장 방법 만 보여줍니다.
Viking
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.