문제

C#의 내부 클래스의 사용 및 구조에 관한 모범 사례는 무엇입니까?

예를 들어, 매우 큰 기본 클래스와 두 개의 큰 내부 클래스가있는 경우 별도의 (부분 클래스) 코드 파일로 나누거나 하나의 큰 다루기 힘든 코드 파일로 남겨 두어야합니까?

또한 대중의 상속 된 내부 수업과 함께 추상적 인 수업을 갖는 것이 나쁜 관행입니까?

도움이 되었습니까?

해결책

일반적으로 나는 두 가지 목적 중 하나를 위해 내부 등급을 예약합니다.

  1. 부모 클래스가 부모 클래스가 하나 이상의 추상적 인 메소드를 갖는 추상적 인 기본 구현이며 각 서브 클래스는 특정 구현을 제공하는 구현입니다. 프레임 워크 디자인과 지침을 읽은 후 나는 이것이 "피하는"것으로 표시되어 있음을 알 수 있지만, 나는 열거와 유사한 시나리오에서 그것을 사용합니다.

  2. 내부 클래스는 비공개이며 비즈니스 로직 단위이거나 다른 클래스에서 소비되거나 사용될 때 근본적으로 깨진 방식으로 부모 클래스와 밀접하게 결합됩니다.

다른 모든 경우에 나는 "메인"클래스보다 약간 덜 친근한 이름을 가진 동일한 네임 스페이스와 소비자/논리적 부모와 동일한 접근성 수준을 유지하려고합니다.

큰 프로젝트에서는 첫 번째 또는 기본 목적이기 때문에 강력하게 결합 된 구성 요소를 처음 구축하는 데 얼마나 자주 자신을 발견 할 수 있는지 놀랄 것입니다. 시야에서 다른 구성 요소가 소비 할 수 있도록 클래스를 노출시키는 데 거의 해를 끼치 지 않습니다.

편집하다 하위 클래스에 대해 이야기하고 있지만 더 잘 설계되고 느슨하게 결합 된 구성 요소가되어야합니다. 클래스간에 최소한의 "표면적"을 유지하는 것이 개인적이고 외부 세계에 보이지 않더라도 향후 확장 또는 변경을위한 코드의 유지 관리가 크게 완화됩니다.

다른 팁

나는 책을 손에 넣을 수 없지만 프레임 워크 디자인 가이드 라인은 public 클라이언트가 클래스 이름을 참조 할 필요가없는 한 내부 클래스. private 내부 수업은 괜찮습니다. 아무도 이것을 알아 차리지 않을 것입니다.

나쁜: ListView.ListViewItemCollection collection = new ListView.ListViewItemCollection();

좋은: listView.Items.Add(...);

대규모 수업과 관련하여 : 일반적으로 이와 같은 것을 작은 클래스로 나누는 것은 각각 하나의 특정 기능이 있습니다. 처음에 그것을 깨는 것은 어렵지만 나중에 당신의 인생이 더 쉬워 질 것으로 예상합니다 ...

일반적으로 내면의 수업은 비공개이어야하며 클래스가 포함 된 수업에서만 사용할 수 있어야합니다. 그들이 내면의 수업이 매우 크면 그들 자신의 수업이어야한다고 제안합니다.

일반적으로 큰 내부 수업이있을 때 내부 클래스가 클래스가 포함되어 있고 개인 방법에 대한 액세스가 필요하기 때문입니다.

나는 이것이 다소 주관적이라고 생각하지만 아마도 "호스트"클래스를 부분적으로 만들어 별도의 코드 파일로 나눌 것입니다.

이렇게하면 더 많은 개요를 얻을 수 있습니다. 프로젝트 파일 편집 파일 그룹을 Windows 양식의 디자이너 클래스처럼 만들기 위해. 나는 당신을 위해 이것을 자동으로하는 Visual Studio addin을 보았다고 생각하지만 어디를 기억하지 못합니다.

편집하다:
일부 보인 후 VSCOMMANDS라는이 작업을 수행하기위한 Visual Studio 추가 기능을 찾았습니다.

그러한 짐승을 구성하는 방법에 대해서만 ...

부분 클래스를 사용하여 메인 클래스와 중첩 클래스를 분할 할 수 있습니다. 그렇게하면 파일을 적절하게 이름을 지정하는 것이 좋습니다. 그래서 무슨 일이 일어나고 있는지 분명합니다.

// main class in file Outer.cs
namespace Demo
{
  public partial class Outer
  {
     // Outer class
  }
}

// nested class in file Outer.Nested1.cs
namespace Demo
{
  public partial class Outer
  {
    private class Nested1
    {
      // Nested1 details
    }
  }
}

거의 같은 방식으로, 당신은 종종 자신의 파일에 (명시 적) 인터페이스를 볼 수 있습니다. 예를 들어 Outer.ISomeInterface.cs 편집기 기본값보다는 #region그들을 ing.

프로젝트 파일 구조는 모양이 시작됩니다

   /Project/Demo/ISomeInterface.cs
   /Project/Demo/Outer.cs
   /Project/Demo/Outer.Nested1.cs
   /Project/Demo/Outer.ISomeInterface.cs

일반적으로 우리 가이 작업을 수행 할 때는 빌더 패턴의 변형입니다.

나는 개인적으로 파일 당 하나의 클래스와 해당 파일의 일부로 내부 클래스를 갖는 것을 좋아합니다. 나는 내면의 수업이 일반적으로 (거의 항상) 개인이어야하며 클래스의 구현 세부 사항이라고 생각합니다. 별도의 파일에 있으면 IMO가 혼란 스럽습니다.

코드 영역을 사용하여 내부 클래스를 감싸고 세부 사항을 숨기는 것은 나에게 잘 어울리고 파일이 작업하기가 어려워집니다. 코드 영역은 내부 클래스를 "숨겨진"상태로 유지하며 개인 구현 세부 사항이므로 나에게 괜찮습니다.

나는 개인적으로 내부 수업을 사용하여 수업 내에서만 내부적으로 사용되는 일부 개념과 작업을 캡슐화합니다. 이런 식으로 나는 해당 클래스의 비공개 API를 오염시키지 않고 API를 깨끗하고 컴팩트하게 유지합니다.

부분적 클래스를 활용하여 이러한 내부 클래스의 정의를 더 나은 Orgnanization을 위해 다른 파일로 옮길 수 있습니다. vs는 asp.net, winform 양식 등과 같은 일부 템플릿 항목을 제외하고는 부분 클래스 파일을 자동으로 그룹화하지 않습니다. 프로젝트 파일을 편집하고 일부 변경해야합니다. 기존 그룹 중 하나를보고 어떻게 수행되는지 확인할 수 있습니다. 솔루션 탐색기에서 부분 클래스 파일을 그룹화 할 수있는 매크로가 있다고 생각합니다.

제 생각에는 내면의 수업은 필요한 경우 작게 유지하고 해당 수업에서만 내부적으로 사용해야합니다. .NET 프레임 워크에서 Relfector를 사용하는 경우 해당 목적으로 만 많이 사용하는 것을 볼 수 있습니다.

내면의 수업이 너무 커지면 유지 가능성 만 있으면 별도의 클래스/코드 파일로 분명히 옮길 것입니다. 누군가가 내부 수업 내에서 내부 수업을 사용하는 것이 좋은 아이디어라고 생각한 기존 코드를 지원해야합니다. 이로 인해 내부 클래스 계층 구조는 4-5 레벨 깊이를 실행했습니다. 말할 것도 없이이 코드는 뚫을 수 없으며보고있는 것을 이해하는 데 오랜 시간이 걸립니다.

여기에서는 중첩 클래스의 실질적인 예를 참조하십시오.

namespace CoreLib.Helpers
{
    using System;
    using System.Security.Cryptography;

    public static class Rnd
    {
        private static readonly Random _random = new Random();

        public static Random Generator { get { return _random; } }

        static Rnd()
        {
        }

        public static class Crypto
        {
            private static readonly RandomNumberGenerator _highRandom = RandomNumberGenerator.Create();

            public static RandomNumberGenerator Generator { get { return _highRandom; } }

            static Crypto()
            {
            }

        }

        public static UInt32 Next(this RandomNumberGenerator value)
        {
            var bytes = new byte[4];
            value.GetBytes(bytes);

            return BitConverter.ToUInt32(bytes, 0);
        }
    }
}

[TestMethod]
public void Rnd_OnGenerator_UniqueRandomSequence()
{
    var rdn1 = Rnd.Generator;
    var rdn2 = Rnd.Generator;
    var list = new List<Int32>();
    var tasks = new Task[10];
    for (var i = 0; i < 10; i++)
    {
        tasks[i] = Task.Factory.StartNew((() =>
        {
            for (var k = 0; k < 1000; k++)
            {
                lock (list)
                {
                    list.Add(Rnd.Generator.Next(Int32.MinValue, Int32.MaxValue));
                }
            }
        }));
    }
    Task.WaitAll(tasks);
    var distinct = list.Distinct().ToList();
    Assert.AreSame(rdn1, rdn2);
    Assert.AreEqual(10000, list.Count);
    Assert.AreEqual(list.Count, distinct.Count);
}

[TestMethod]
public void Rnd_OnCryptoGenerator_UniqueRandomSequence()
{
    var rdn1 = Rnd.Crypto.Generator;
    var rdn2 = Rnd.Crypto.Generator;
    var list = new ConcurrentQueue<UInt32>();
    var tasks = new Task[10];
    for (var i = 0; i < 10; i++)
    {
        tasks[i] = Task.Factory.StartNew((() =>
        {
            for (var k = 0; k < 1000; k++)
            {
                    list.Enqueue(Rnd.Crypto.Generator.Next());
            }
        }));
    }
    Task.WaitAll(tasks);
    var distinct = list.Distinct().ToList();
    Assert.AreSame(rdn1, rdn2);
    Assert.AreEqual(10000, list.Count);
    Assert.AreEqual(list.Count, distinct.Count);
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top