C #이 인덱싱 된 속성을 구현하지 않는 이유는 무엇입니까?


83

알아요, 알아요 ... 이런 종류의 질문에 대한 Eric Lippert의 대답은 일반적으로 " 디자인, 구현, 테스트 및 문서화 비용이 들지 않았기 때문에 "와 같습니다 .

하지만 여전히 더 나은 설명이 필요합니다 ... 새로운 C # 4 기능에 대한이 블로그 게시물 을 읽고 있었으며 COM Interop에 대한 섹션에서 다음 부분이 내 관심을 끌었습니다.

그런데이 코드는 색인 된 속성 (범위 뒤의 대괄호를 자세히 살펴보십시오.)이라는 새로운 기능을 하나 더 사용합니다. 그러나이 기능은 COM interop에서만 사용할 수 있습니다. C # 4.0에서는 고유 한 인덱싱 된 속성을 만들 수 없습니다 .

좋아, 그런데 왜? C #에서 인덱싱 된 속성을 만들 수 없다는 것을 이미 알고 후회했지만이 문장은 다시 생각하게했습니다. 이를 구현해야하는 몇 가지 좋은 이유를 볼 수 있습니다.

  • CLR이이를 지원하므로 (예 PropertyInfo.GetValue: index매개 변수가 있음) C #에서이를 활용할 수없는 것은 유감입니다.
  • 문서에 표시된대로 COM interop에 대해 지원됩니다 (동적 디스패치 사용).
  • VB.NET에서 구현됩니다.
  • 인덱서를 생성하는 것은 이미 가능합니다. 즉, 객체 자체에 인덱스를 적용하는 것이 가능합니다. 따라서 동일한 구문을 유지하고 this속성 이름으로 만 대체하여 아이디어를 속성으로 확장하는 것은 큰 문제가 아닐 것입니다.

그런 종류의 것을 작성할 수 있습니다.

public class Foo
{
    private string[] _values = new string[3];
    public string Values[int index]
    {
        get { return _values[index]; }
        set { _values[index] = value; }
    }
}

현재 내가 아는 유일한 해결 방법 ValuesCollection은 인덱서를 구현 하는 내부 클래스 ( 예 :)를 만들고 Values해당 내부 클래스의 인스턴스를 반환하도록 속성을 변경 하는 것입니다.

이것은 매우 쉽지만 성가신 일입니다 ... 그래서 아마도 컴파일러가 우리를 위해 할 수있을 것입니다! 옵션은 인덱서를 구현하는 내부 클래스를 생성하고 공용 일반 인터페이스를 통해 노출하는 것입니다.

// interface defined in the namespace System
public interface IIndexer<TIndex, TValue>
{
    TValue this[TIndex index]  { get; set; }
}

public class Foo
{
    private string[] _values = new string[3];

    private class <>c__DisplayClass1 : IIndexer<int, string>
    {
        private Foo _foo;
        public <>c__DisplayClass1(Foo foo)
        {
            _foo = foo;
        }

        public string this[int index]
        {
            get { return _foo._values[index]; }
            set { _foo._values[index] = value; }
        }
    }

    private IIndexer<int, string> <>f__valuesIndexer;
    public IIndexer<int, string> Values
    {
        get
        {
            if (<>f__valuesIndexer == null)
                <>f__valuesIndexer = new <>c__DisplayClass1(this);
            return <>f__valuesIndexer;
        }
    }
}

그러나 물론이 경우 속성은 실제로를 반환하고 실제로IIndexer<int, string> 인덱싱 된 속성이 아닙니다. 실제 CLR 인덱싱 된 속성을 생성하는 것이 좋습니다.

어떻게 생각해 ? 이 기능을 C #에서 보시겠습니까? 그렇지 않다면 왜?


1
나는 이것이 "우리는 X에 대한 요청을 받지만 Y에 대한 요청을받는 것" 의 또 다른 문제라는 느낌 을받습니다 .
ChaosPandion

1
@ChaosPandion, 맞아요 ...하지만이 기능은 구현하기가 매우 쉬울 것입니다. 확실히 "필수"항목은 아니지만 "가지고 있어도 좋은"범주에 속합니다
Thomas Levesque

4
인덱서는 이미 CLR 관점에서 약간 짜증이납니다. 이제 모든 속성이 잠재적으로 인덱서 매개 변수를 가질 수 있으므로 속성 작업을 원하는 코드에 새로운 경계 케이스를 추가합니다. 인덱서가 일반적으로 나타내는 개념은 개체의 속성이 아니라 '내용'이기 때문에 C # 구현이 의미가 있다고 생각합니다. 임의의 인덱서 속성을 제공하는 경우 클래스에 서로 다른 콘텐츠 그룹이있을 수 있음을 의미하므로 복잡한 하위 콘텐츠를 새 클래스로 캡슐화하게됩니다. 내 질문은 : CLR이 인덱싱 된 속성을 제공하는 이유는 무엇입니까?
Dan Bryant

1
@tk_ 건설적인 의견에 감사드립니다. 프리 파스칼이 아닌 언어에 대한 모든 게시물에 유사한 댓글을 게시하고 있습니까? 글쎄, 난 ... 당신이 자신에 대해 좋은 느낌 희망
토마스 레베

3
이것은 C ++ / CLI 및 VB.net이 C #보다 나은 몇 안되는 상황 중 하나입니다. 내 C ++ / CLI 코드에서 많은 인덱싱 된 속성을 구현했으며 이제이를 C #으로 변환 할 때 모두에 대한 해결 방법을 찾아야합니다. :-( 형편 없어! 귀하 // 그것은 사물의 종류를 작성할 수 있습니다 것입니다 것은 내가 년 동안 한 일이다.
토비아스 Knauss

답변:


122

C # 4를 디자인 한 방법은 다음과 같습니다.

먼저 언어에 추가 할 수있는 모든 가능한 기능 목록을 만들었습니다.

그런 다음 기능을 "이건 나쁘다, 절대하지 말아야한다", "이건 대단해, ​​우리가해야 해", "이건 좋지만 이번에는하지 말자"로 분류했습니다.

그런 다음 "가져야 할"기능을 설계, 구현, 테스트, 문서화, 제공 및 유지 관리하는 데 얼마나 많은 예산이 필요한지 살펴본 결과 예산이 100 % 초과되었음을 발견했습니다.

그래서 우리는 "갖고있는"물통에서 "갖기 좋은"물통으로 많은 것들을 옮겼습니다.

인덱스 속성은 결코 어느 곳이었다 근처 은 "이 꼭"목록의 맨. 그들은 "좋은"목록에서 매우 낮고 "나쁜 생각"목록에 유혹합니다.

우리가 멋진 기능 X를 디자인, 구현, 테스트, 문서화 또는 유지하는 데 소비하는 1 분은 멋진 기능 A, B, C, D, E, F 및 G에 소비 할 수없는 시간입니다. 가능한 최고의 기능을 수행하십시오. 인덱싱 된 속성은 좋지만 실제로 구현하기에 충분할만큼 좋은 것은 아닙니다.


20
불량 목록에 추가 할 수 있습니까? 인덱서를 구현하는 중첩 형식을 노출 할 수있을 때 현재 구현이 얼마나 제한적인지 실제로 알 수 없습니다. 나는 당신이 메서드가되어야하는 데이터 바인딩과 속성에 무언가를 넣는 것을 시도하기 위해 많은 해커를보기 시작할 것이라고 상상할 것이다.
Josh

11
그리고 자동 구현 된 INotifyPropertyChanged가 인덱싱 된 속성보다 목록에서 훨씬 더 높기를 바랍니다. :)
Josh

3
@Eric, 좋아, 내가 의심했던 것입니다 ... 어쨌든 대답 해 주셔서 감사합니다! 나는 몇 년 동안했던 것처럼 나는, 인덱스 속성없이 살 수있는 것 같아요;)
토마스 레베에게

5
@Martin : 저는 대규모 소프트웨어 팀의 예산을 결정하는 전문가가 아닙니다. 귀하의 질문은 Soma, Jason Zander 또는 Scott Wiltamuth에게 전달되어야합니다. 저는 모두가 블로그를 가끔 작성한다고 믿습니다. Scala와의 비교는 사과와 오렌지의 비교입니다. Scala는 C #이하는 대부분의 비용을 가지고 있지 않습니다. 하나만 말하면 이전 버전과의 호환성 요구 사항이 매우 중요한 수백만 명의 사용자가 없습니다. C #과 Scala간에 막대한 비용 차이를 유발할 수있는 더 많은 요인을 말씀 드릴 수 있습니다.
Eric Lippert 2010-08-17

12
+1 : 사람들은 이것을 읽으면 소프트웨어 프로젝트 관리에 대해 많은 것을 배울 수 있습니다. 그리고 그것은 단지 몇 줄 밖에되지 않습니다.
Brian MacKay

22

AC # 인덱서 인덱싱 된 속성입니다. Item기본적으로 이름이 지정 되며 (예 : VB에서 참조 할 수 있음) 원하는 경우 IndexerNameAttribute로 변경할 수 있습니다 .

구체적으로 왜 그렇게 설계되었는지는 잘 모르겠지만 의도적 인 제한 인 것 같습니다. 그러나 이는 멤버 컬렉션에 대해 인덱싱 가능한 개체를 반환하는 인덱싱되지 않은 속성의 접근 방식을 권장하는 프레임 워크 디자인 지침과 일치합니다. 즉, "인덱싱 가능"은 유형의 특성입니다. 여러 가지 방법으로 인덱싱 할 수있는 경우 실제로 여러 유형으로 분할해야합니다.


1
감사합니다! this [int]로 가져 오지만 이름이 원래 "Item"이 아닌 (때로는 "item"또는 "value"또는 이와 유사한) 기본 indxer (DISPID 0)가있는 COM interop 인터페이스를 구현할 때 반복적으로 오류가 발생했습니다. ). 어쨌든 컴파일되고 실행되지만 이름이 맞지 않기 때문에 FxCop CA1033 InterfaceMethodsShouldBeCallableByChildTypes 경고, CLS 준수 문제 (대소 문자 만 다른 식별자) 등이 발생합니다. [IndexerName] 만 있으면되지만 찾지 못했습니다.
puetzk

감사합니다!!! IndexerName 특성을 사용하면 MSIL 서명을 손상시키지 않고 VB 어셈블리를 C #으로 변환 할 수 있습니다.
존 Tirjan

15

이미 할 수 있고 OO 측면에서 생각해야하기 때문에 색인 된 속성을 추가하면 언어에 더 많은 소음이 추가됩니다. 그리고 다른 일을하는 또 다른 방법입니다.

class Foo
{
    public Values Values { ... }
}

class Values
{
    public string this[int index] { ... }    
}

foo.Values[0]

저는 개인적으로 10 가지 방법보다는 한 가지 방법 만보고 싶습니다. 그러나 물론 이것은 주관적인 의견입니다.


2
+1, 이것은 VB5 구조로 언어를 보완하는 것보다 구현하는 훨씬 더 좋은 방법입니다.
Josh

1
+1 왜냐하면 이것이 내가하는 방법이기 때문입니다. 그리고 당신이 훌륭하다면 아마 이것을 제네릭으로 만들 수있을 것입니다.
Tony

10
이 접근 방식의 한 가지 문제는 다른 코드가 인덱서의 복사본을 만들 수 있다는 것이며, 그렇게한다면 의미론이 무엇인지 명확하지 않습니다. 코드가 "var userList = Foo.Users; Foo.RemoveSomeUsers (); someUser = userList [5];"인 경우 Foo의 요소 [5] (RemoveSomeUsers 이전) 또는 이후 요소 여야합니까? 하나의 userList []가 인덱싱 된 속성이면 직접 노출 할 필요가 없습니다.
supercat 2011-12-05

일시적인 할당을 좋아합니까?
Joshua

9

나는 색인 된 속성에 대한 아이디어를 선호했지만 그것이 끔찍한 모호성을 추가하고 실제로 기능성을 떨어 뜨릴 것이라는 것을 깨달았습니다 . 인덱싱 된 속성은 자식 컬렉션 인스턴스가 없음을 의미합니다. 그것은 좋고 나쁘다. 구현하는 데 문제가 적고 둘러싸는 소유자 클래스에 대한 참조가 필요하지 않습니다. 그러나 그것은 또한 당신이 그 자식 컬렉션을 아무것도 전달할 수 없다는 것을 의미합니다. 매번 열거해야 할 것입니다. 당신은 그것에 대해 foreach를 할 수 없습니다. 무엇보다도 인덱싱 된 속성을 살펴보면 그 속성인지 컬렉션 속성인지 알 수 없습니다.

이 아이디어는 합리적이지만 유연성이 떨어지고 갑작스러운 어색함으로 이어집니다.


대답이 조금 늦더라도 흥미로운 생각입니다.). 당신은 아주 좋은 점수를 얻고 있습니다. +1.
Thomas Levesque

vb.net에서 클래스는 같은 이름의 인덱싱 된 속성과 인덱싱되지 않은 속성을 모두 가질 수 있습니다 [예 Bar]. 식은에 Thing.Bar(5)인덱싱 된 속성 Bar을 사용 Thing하는 반면 (Thing.Bar)(5)인덱싱되지 않은 속성 Bar을 사용한 다음 결과 객체의 기본 인덱서를 사용합니다. 내 결속에 따르면 Thing.Bar[5]의 속성이 Thing아니라 의 속성이되도록 허용 하는 Thing.Bar것은 좋은데, 그 이유는 무엇보다도 언젠가는의 의미 Thing.Bar[4]가 명확 할 수 있지만 적절한 효과는 ...
supercat

... 같은 var temp=Thing.Bar; do_stuff_with_thing; var q=temp[4]것이 명확하지 않을 수 있습니다. 공유 불변 객체 또는 비공유 변경 객체 일 수있는 필드에 Thing데이터 Bar를 보유 할 수 있는 개념도 고려하십시오 . Bar지원 필드가 변경 불가능할 때 쓰기 시도 는 변경 가능한 사본을 작성해야하지만 읽기 시도는 변경되지 않아야합니다. 경우 Bar명명 된 인덱스 속성입니다 필요한 경우 세터가 새로운 변경 가능한 사본을 만들 수있는 반면, 인덱스 게터는 (변경 가능 여부) 만 백업 수집을 남길 수 있습니다.
supercat 2013-04-02

속성의 인덱서는 열거되지 않습니다 - 그것은 핵심이다
조지 Birbilis

6

깔끔하고 간결한 코드를 작성하려고 할 때 색인화 된 속성이 부족하다는 사실을 알게되었습니다. 인덱싱 된 속성은 인덱싱 된 클래스 참조를 제공하거나 개별 메서드를 제공하는 것과는 매우 다른 의미를 갖습니다. 인덱싱 된 속성을 구현하는 내부 개체에 대한 액세스를 제공하는 것은 종종 개체 방향의 핵심 구성 요소 중 하나 인 캡슐화를 깨뜨리기 때문에 허용되는 것으로 간주된다는 사실이 약간 혼란 스럽습니다.

이 문제가 자주 발생하지만 오늘 다시 만났으므로 실제 코드 예제를 제공하겠습니다. 작성되는 인터페이스와 클래스는 느슨하게 관련된 정보의 모음 인 애플리케이션 구성을 저장합니다. 명명 된 스크립트 조각을 추가해야했고 명명되지 않은 클래스 인덱서를 사용하면 스크립트 조각이 구성의 일부일 뿐이므로 매우 잘못된 컨텍스트를 암시했을 것입니다.

C #에서 인덱싱 된 속성을 사용할 수 있다면 아래 코드를 구현할 수있었습니다 (구문은 this [key]가 PropertyName [key]로 변경됨).

public interface IConfig
{
    // Other configuration properties removed for examp[le

    /// <summary>
    /// Script fragments
    /// </summary>
    string Scripts[string name] { get; set; }
}

/// <summary>
/// Class to handle loading and saving the application's configuration.
/// </summary>
internal class Config : IConfig, IXmlConfig
{
  #region Application Configuraiton Settings

    // Other configuration properties removed for examp[le

    /// <summary>
    /// Script fragments
    /// </summary>
    public string Scripts[string name]
    {
        get
        {
            if (!string.IsNullOrWhiteSpace(name))
            {
                string script;
                if (_scripts.TryGetValue(name.Trim().ToLower(), out script))
                    return script;
            }
            return string.Empty;
        }
        set
        {
            if (!string.IsNullOrWhiteSpace(name))
            {
                _scripts[name.Trim().ToLower()] = value;
                OnAppConfigChanged();
            }
        }
    }
    private readonly Dictionary<string, string> _scripts = new Dictionary<string, string>();

  #endregion

    /// <summary>
    /// Clears configuration settings, but does not clear internal configuration meta-data.
    /// </summary>
    private void ClearConfig()
    {
        // Other properties removed for example
        _scripts.Clear();
    }

  #region IXmlConfig

    void IXmlConfig.XmlSaveTo(int configVersion, XElement appElement)
    {
        Debug.Assert(configVersion == 2);
        Debug.Assert(appElement != null);

        // Saving of other properties removed for example

        if (_scripts.Count > 0)
        {
            var scripts = new XElement("Scripts");
            foreach (var kvp in _scripts)
            {
                var scriptElement = new XElement(kvp.Key, kvp.Value);
                scripts.Add(scriptElement);
            }
            appElement.Add(scripts);
        }
    }

    void IXmlConfig.XmlLoadFrom(int configVersion, XElement appElement)
    {
        // Implementation simplified for example

        Debug.Assert(appElement != null);
        ClearConfig();
        if (configVersion == 2)
        {
            // Loading of other configuration properites removed for example

            var scripts = appElement.Element("Scripts");
            if (scripts != null)
                foreach (var script in scripts.Elements())
                    _scripts[script.Name.ToString()] = script.Value;
        }
        else
            throw new ApplicaitonException("Unknown configuration file version " + configVersion);
    }

  #endregion
}

불행히도 인덱싱 된 속성은 구현되지 않았으므로이를 저장하는 클래스를 구현하고 이에 대한 액세스를 제공했습니다. 이 도메인 모델에서 구성 클래스의 목적은 모든 세부 정보를 캡슐화하는 것이기 때문에 이는 바람직하지 않은 구현입니다. 이 클래스의 클라이언트는 이름으로 특정 스크립트 조각에 액세스하며이를 계산하거나 열거 할 이유가 없습니다.

나는 이것을 다음과 같이 구현할 수 있었다.

public string ScriptGet(string name)
public void ScriptSet(string name, string value)

내가 가져야 할 것 같지만, 이는이 누락 된 기능을 대체하기 위해 인덱스 된 클래스를 사용하는 것이 종종 합리적인 대체물이 아닌 이유를 보여주는 유용한 예시입니다.

인덱싱 된 속성과 유사한 기능을 구현하기 위해 아래 코드를 작성해야했습니다.이 코드는 상당히 길고 복잡하며 읽기, 이해 및 유지 관리가 더 어렵습니다.

public interface IConfig
{
    // Other configuration properties removed for examp[le

    /// <summary>
    /// Script fragments
    /// </summary>
    ScriptsCollection Scripts { get; }
}

/// <summary>
/// Class to handle loading and saving the application's configuration.
/// </summary>
internal class Config : IConfig, IXmlConfig
{
    public Config()
    {
        _scripts = new ScriptsCollection();
        _scripts.ScriptChanged += ScriptChanged;
    }

  #region Application Configuraiton Settings

    // Other configuration properties removed for examp[le

    /// <summary>
    /// Script fragments
    /// </summary>
    public ScriptsCollection Scripts
    { get { return _scripts; } }
    private readonly ScriptsCollection _scripts;

    private void ScriptChanged(object sender, ScriptChangedEventArgs e)
    {
        OnAppConfigChanged();
    }

  #endregion

    /// <summary>
    /// Clears configuration settings, but does not clear internal configuration meta-data.
    /// </summary>
    private void ClearConfig()
    {
        // Other properties removed for example
        _scripts.Clear();
    }

  #region IXmlConfig

    void IXmlConfig.XmlSaveTo(int configVersion, XElement appElement)
    {
        Debug.Assert(configVersion == 2);
        Debug.Assert(appElement != null);

        // Saving of other properties removed for example

        if (_scripts.Count > 0)
        {
            var scripts = new XElement("Scripts");
            foreach (var kvp in _scripts)
            {
                var scriptElement = new XElement(kvp.Key, kvp.Value);
                scripts.Add(scriptElement);
            }
            appElement.Add(scripts);
        }
    }

    void IXmlConfig.XmlLoadFrom(int configVersion, XElement appElement)
    {
        // Implementation simplified for example

        Debug.Assert(appElement != null);
        ClearConfig();
        if (configVersion == 2)
        {
            // Loading of other configuration properites removed for example

            var scripts = appElement.Element("Scripts");
            if (scripts != null)
                foreach (var script in scripts.Elements())
                    _scripts[script.Name.ToString()] = script.Value;
        }
        else
            throw new ApplicaitonException("Unknown configuration file version " + configVersion);
    }

  #endregion
}

public class ScriptsCollection : IEnumerable<KeyValuePair<string, string>>
{
    private readonly Dictionary<string, string> Scripts = new Dictionary<string, string>();

    public string this[string name]
    {
        get
        {
            if (!string.IsNullOrWhiteSpace(name))
            {
                string script;
                if (Scripts.TryGetValue(name.Trim().ToLower(), out script))
                    return script;
            }
            return string.Empty;
        }
        set
        {
            if (!string.IsNullOrWhiteSpace(name))
                Scripts[name.Trim().ToLower()] = value;
        }
    }

    public void Clear()
    {
        Scripts.Clear();
    }

    public int Count
    {
        get { return Scripts.Count; }
    }

    public event EventHandler<ScriptChangedEventArgs> ScriptChanged;

    protected void OnScriptChanged(string name)
    {
        if (ScriptChanged != null)
        {
            var script = this[name];
            ScriptChanged.Invoke(this, new ScriptChangedEventArgs(name, script));
        }
    }

  #region IEnumerable

    public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
    {
        return Scripts.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

  #endregion
}

public class ScriptChangedEventArgs : EventArgs
{
    public string Name { get; set; }
    public string Script { get; set; }

    public ScriptChangedEventArgs(string name, string script)
    {
        Name = name;
        Script = script;
    }
}

2

다른 해결 방법은 더 적은 작업이 필요한 C # 인덱싱을 지원하는 속성의 손쉬운 생성에 나열되어 있습니다.

편집 : 또한 원래 질문에 대한 응답으로 라이브러리 지원을 통해 원하는 구문을 수행 할 수 있다면 언어에 직접 추가하기 위해 매우 강력한 사례가 필요하다고 생각합니다. 언어 팽창을 최소화하십시오.


2
당신의 편집에 대한 대답 : 나는 그것이 언어를 부풀게 할 것이라고 생각하지 않습니다. 클래스 인덱서 ( this[])에 대한 구문이 이미 존재하며 , 대신 식별자를 허용하면됩니다 this. 그러나 나는 이유는 그의 대답에 에릭에 의해 설명을 위해 그 어느 언어에 포함됩니다 의심
토마스 레베을

1

디자인, 구현, 테스트 및 문서화 비용이 들지 않았기 때문에 추가하지 않았다고 말하고 싶습니다.

농담을 제쳐두고, 아마도 해결 방법이 간단하고 기능이 시간 대비 이익을 줄이지 않기 때문일 것입니다. 나는 이것이 라인 아래의 변화로 나타나는 것을보고 놀라지 않을 것입니다.

더 쉬운 해결 방법은 일반적인 방법을 만드는 것임을 언급하는 것을 잊었습니다.

public void SetFoo(int index, Foo toSet) {...}
public Foo GetFoo(int index) {...}

매우 사실입니다. 속성 구문이 절대적으로 중요하다면 Ion의 해결 방법을 사용할 수 있습니다 (아마도 다양한 반환 유형을 허용하는 일부 제네릭과 함께). 그럼에도 불구하고 이것은 추가 언어 기능없이 동일한 작업을 수행하기가 상대적으로 쉽다는 것을 의미한다고 생각합니다.
Ron Warholic

1

인덱싱 기능을 프록시하기 위해 람다를 사용하는 간단한 일반 솔루션이 있습니다.

읽기 전용 인덱싱

public class RoIndexer<TIndex, TValue>
{
    private readonly Func<TIndex, TValue> _Fn;

    public RoIndexer(Func<TIndex, TValue> fn)
    {
        _Fn = fn;
    }

    public TValue this[TIndex i]
    {
        get
        {
            return _Fn(i);
        }
    }
}

가변 인덱싱

public class RwIndexer<TIndex, TValue>
{
    private readonly Func<TIndex, TValue> _Getter;
    private readonly Action<TIndex, TValue> _Setter;

    public RwIndexer(Func<TIndex, TValue> getter, Action<TIndex, TValue> setter)
    {
        _Getter = getter;
        _Setter = setter;
    }

    public TValue this[TIndex i]
    {
        get
        {
            return _Getter(i);
        }
        set
        {
            _Setter(i, value);
        }
    }
}

그리고 공장

public static class Indexer
{
    public static RwIndexer<TIndex, TValue> Create<TIndex, TValue>(Func<TIndex, TValue> getter, Action<TIndex, TValue> setter)
    {
        return new RwIndexer<TIndex, TValue>(getter, setter);
    } 
    public static RoIndexer<TIndex, TValue> Create<TIndex, TValue>(Func<TIndex, TValue> getter)
    {
        return new RoIndexer<TIndex, TValue>(getter);
    } 
}

내 자신의 코드에서 나는 그것을 다음과 같이 사용한다.

public class MoineauFlankContours
{

    public MoineauFlankContour Rotor { get; private set; }

    public MoineauFlankContour Stator { get; private set; }

     public MoineauFlankContours()
    {
        _RoIndexer = Indexer.Create(( MoineauPartEnum p ) => 
            p == MoineauPartEnum.Rotor ? Rotor : Stator);
    }
    private RoIndexer<MoineauPartEnum, MoineauFlankContour> _RoIndexer;

    public RoIndexer<MoineauPartEnum, MoineauFlankContour> FlankFor
    {
        get
        {
            return _RoIndexer;
        }
    }

}

MoineauFlankContours 인스턴스를 사용하면

MoineauFlankContour rotor = contours.FlankFor[MoineauPartEnum.Rotor];
MoineauFlankContour stator = contours.FlankFor[MoineauPartEnum.Stator];

영리하지만 아마 그것에게 모든 시간을 만드는 대신 인덱서를 캐시하는 것이 좋습니다 것)
토마스 레베

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.