[c#] 제네릭의 멋진 점은 무엇이며 왜 사용합니까?

나는이 소프트볼을 공원에서 치고 싶은 사람에게 줄 것이라고 생각했다. 제네릭이란 무엇이며 제네릭의 장점은 무엇이며 왜, 어디서, 어떻게 사용해야합니까? 상당히 기본적으로 유지하십시오. 감사.



답변

  • 형식이 안전한 코드 / 사용 라이브러리 메서드를 작성할 수 있습니다. 즉, List <string>은 문자열 목록이됩니다.
  • 제네릭이 사용 된 결과 컴파일러는 유형 안전성을 위해 코드에서 컴파일 시간 검사를 수행 할 수 있습니다. 즉, 문자열 목록에 int를 넣으려고합니까? ArrayList를 사용하면 덜 투명한 런타임 오류가 발생합니다.
  • boxing / unboxing (.net이 값 유형을 참조 유형으로 또는 그 반대로 변환해야하는 경우 )을 피하거나 객체에서 필수 참조 유형으로 캐스팅 하는 것을 방지하므로 객체를 사용하는 것보다 빠릅니다 .
  • 동일한 기본 동작을 가진 많은 유형에 적용 할 수있는 코드를 작성할 수 있습니다. 즉, Dictionary <string, int>는 Dictionary <DateTime, double>과 동일한 기본 코드를 사용합니다. 제네릭을 사용하여 프레임 워크 팀은 앞서 언급 한 이점과 함께 두 가지 결과를 모두 달성하기 위해 하나의 코드 만 작성하면되었습니다.


답변

반복하는 게 정말 싫어요. 나는 내가해야하는 것보다 더 자주 같은 것을 입력하는 것을 싫어한다. 나는 약간의 차이를 가지고 여러 번 재조정하는 것을 좋아하지 않습니다.

만드는 대신 :

class MyObjectList  {
   MyObject get(int index) {...}
}
class MyOtherObjectList  {
   MyOtherObject get(int index) {...}
}
class AnotherObjectList  {
   AnotherObject get(int index) {...}
}

재사용 가능한 클래스 하나를 만들 수 있습니다 … (어떤 이유로 원시 컬렉션을 사용하지 않으려는 경우)

class MyList<T> {
   T get(int index) { ... }
}

저는 이제 3 배 더 효율적이고 하나의 복사본 만 유지하면됩니다. 더 적은 코드를 유지하고 싶지 않은 이유는 무엇입니까?

이것은 다른 클래스와 상호 작용해야하는 a Callable<T>또는 a 와 같은 컬렉션이 아닌 클래스의 경우에도 마찬가지입니다 Reference<T>. 유형 안전 버전을 만들기 위해 Callable<T>Future<T>기타 모든 관련 클래스를 확장 하시겠습니까?

난 안해.


답변

타입 캐스트를 할 필요가 없다는 것은 컴파일 타임에 타입 검사를 수행하기 때문에 자바 제네릭의 가장 큰 장점 중 하나입니다 . 이것은 ClassCastException런타임에 던질 수있는 s 가능성을 줄이고 더 강력한 코드로 이어질 수 있습니다.

그러나 나는 당신이 그것을 완전히 알고 있다고 생각합니다.

Generics를 볼 때마다 두통이 생깁니다. Java의 가장 좋은 부분은 단순하고 최소한의 구문과 제네릭이 단순하지 않고 상당한 양의 새로운 구문을 추가한다는 점입니다.

처음에는 제네릭의 이점도 보지 못했습니다. 1.4 구문에서 Java를 배우기 시작했습니다 (당시 Java 5가 나왔음에도 불구하고). 제네릭을 접했을 때 작성해야 할 코드가 더 많다는 느낌이 들었고 실제로 이점을 이해하지 못했습니다.

최신 IDE를 사용하면 제네릭으로 코드를 더 쉽게 작성할 수 있습니다.

대부분의 현대적이고 괜찮은 IDE는 제네릭, 특히 코드 완성으로 코드 작성을 지원할만큼 똑똑합니다.

다음 Map<String, Integer>HashMap. 입력해야하는 코드는 다음과 같습니다.

Map<String, Integer> m = new HashMap<String, Integer>();

그리고 실제로 새로운 HashMap. 그러나 실제로 Eclipse가 필요한 것을 알기 전에이 정도만 입력해야했습니다.

Map<String, Integer> m = new Ha Ctrl+Space

사실, HashMap후보 목록에서 선택해야 했지만 기본적으로 IDE는 제네릭 유형을 포함하여 추가 할 항목을 알고있었습니다. 올바른 도구를 사용하면 제네릭을 사용하는 것이 나쁘지 않습니다.

또한 유형이 알려져 있기 때문에 제네릭 컬렉션에서 요소를 검색 할 때 IDE는 해당 개체가 이미 선언 된 유형의 개체 인 것처럼 작동합니다. 개체의 유형을 알기 위해 IDE를 캐스팅 할 필요가 없습니다. 이다.

제네릭의 주요 장점은 새로운 Java 5 기능과 잘 어울리는 방식에서 비롯됩니다. 다음은 정수를 a에 던지고 Set합계를 계산 하는 예입니다 .

Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(42);

int total = 0;
for (int i : set) {
  total += i;
}

이 코드에는 세 가지 새로운 Java 5 기능이 있습니다.

첫째, 프리미티브의 제네릭 및 오토 박싱은 다음 행을 허용합니다.

set.add(10);
set.add(42);

정수는 10으로되어 오토 박싱 Integer의 값 10. (그리고 동일 42). 그런 다음 s 를 보유하는 것으로 알려진에 Integer던져집니다 . 던지려고 하면 컴파일 오류가 발생합니다.SetIntegerString

다음으로 for-each 루프는 다음 세 가지를 모두 취합니다.

for (int i : set) {
  total += i;
}

첫째, Set포함하는 Integers는 for-each 루프에서 사용됩니다. 각 요소는으로 선언 되며은 기본적으로 다시 unboxed int되므로 허용됩니다 . 그리고이 unboxing이 발생한다는 사실은 제네릭을 사용 하여 .IntegerintIntegerSet

Generics는 Java 5에 도입 된 새로운 기능을 결합하는 접착제가 될 수 있으며 코딩을 더 간단하고 안전하게 만듭니다. 그리고 대부분의 경우 IDE는 좋은 제안을 할 수있을만큼 똑똑하므로 일반적으로 더 많이 입력하지 않아도됩니다.

솔직히 Set예제 에서 볼 수 있듯이 Java 5 기능을 사용하면 코드가 더 간결하고 강력해질 수 있다고 생각합니다.

편집-제네릭이없는 예

다음은 Set제네릭을 사용하지 않은 위의 예입니다. 가능하지만 정확히 유쾌하지는 않습니다.

Set set = new HashSet();
set.add(10);
set.add(42);

int total = 0;
for (Object o : set) {
  total += (Integer)o;
}

(참고 : 위 코드는 컴파일 타임에 확인되지 않은 변환 경고를 생성합니다.)

제네릭이 아닌 컬렉션을 사용하는 경우 컬렉션에 입력되는 유형은 유형의 개체입니다 Object. 따라서이 예에서 a Objectadd세트 로 편집되는 것입니다.

set.add(10);
set.add(42);

상기 라인에서 오토 박싱 재생에 – 프리미티브 int10과은 42으로 오토 박싱되는 Integer받는 추가되는 개체 Set. 그러나 컴파일러가 어떤 유형 을 예상 해야하는지 알 수있는 유형 정보가 없기 때문에 Integer객체가 Objects 로 처리된다는 점 을 명심 하십시오 Set.

for (Object o : set) {

이것이 중요한 부분입니다. for-each 루프가 작동하는 이유는 존재하는 경우 with 형식 정보 를 반환하는 인터페이스를 Set구현 하기 때문 입니다. ( , 즉)IterableIteratorIterator<T>

그러나 유형 정보가 없기 때문에는 as 에서 값을 Set반환하는 an Iterator을 반환 하므로 for-each 루프에서 검색되는 요소 는 유형 이어야합니다 .SetObjectObject

에서 Object가 검색 Set되었으므로 Integer추가를 수행하려면 수동으로로 캐스팅해야합니다 .

  total += (Integer)o;

여기서, 타입 변환은에서 수행 Object내지 An Integer. 이 경우, 우리는 이것이 항상 작동한다는 것을 알고 있지만, 수동 형변환은 항상 다른 곳에서 사소한 변경이 이루어지면 손상 될 수있는 취약한 코드라고 느끼게합니다. (나는 모든 타입 캐스트가 ClassCastException일어나기를 기다리고 있다고 느낀다 . 그러나 나는 빠져 나간다 …)

Integer이제로 박싱되어 int상기에 또한 수행 할 수있는 int변수 total.

Java 5의 새로운 기능이 제네릭이 아닌 코드와 함께 사용할 수 있음을 설명 할 수 있기를 바랍니다.하지만 제네릭으로 코드를 작성하는 것만 큼 깨끗하고 간단하지는 않습니다. 그리고 제 생각에는 Java 5의 새로운 기능을 최대한 활용하려면 제네릭을 조사해야합니다. 최소한 컴파일 시간 검사를 통해 잘못된 타입 캐스트가 런타임에 예외를 발생시키는 것을 방지 할 수 있다면 말입니다.


답변

당신은 1.5이 출시되기 직전 자바 버그 데이터베이스를 검색한다면, 당신은 일곱 배나 더 많은 버그를 찾을 것 NullPointerException이상을 ClassCastException. 따라서 버그 나 최소한 약간의 연기 테스트 후에도 지속되는 버그를 찾는 것은 훌륭한 기능이 아닌 것 같습니다.

제네릭의 큰 장점은 코드에 중요한 유형 정보를 문서화한다는 것 입니다. 해당 유형 정보를 코드로 문서화하지 않으려면 동적으로 유형이 지정된 언어를 사용하거나 최소한 더 암시적인 유형 추론이있는 언어를 사용합니다.

객체의 컬렉션을 자체적으로 유지하는 것은 나쁜 스타일이 아닙니다 (하지만 일반적인 스타일은 캡슐화를 효과적으로 무시하는 것입니다). 오히려 당신이하는 일에 달려 있습니다. 콜렉션을 “알고리즘”으로 전달하는 것은 제네릭을 사용하여 (컴파일시 또는 전에) 확인하기가 약간 더 쉽습니다.


답변

Java의 Generics는 파라 메트릭 다형성을 용이하게 합니다. 유형 매개 변수를 사용하여 유형에 인수를 전달할 수 있습니다. String foo(String s)모델 과 같은 메소드 가 특정 문자열뿐만 아니라 모든 문자열에 대한 일부 동작을 s나타내는 것처럼 List<T>모델 과 같은 유형은 특정 유형뿐만 아니라 모든 유형에 대한 일부 동작을 나타냅니다 . 모든 유형 대해 요소가 s 유형이List<T> 있다고 말합니다 . 그래서 실제로 유형 생성자 입니다. 유형을 인수로 취하고 결과로 다른 유형을 구성합니다.TListTList

다음은 내가 매일 사용하는 제네릭 유형의 몇 가지 예입니다. 첫째, 매우 유용한 일반 인터페이스 :

public interface F<A, B> {
  public B f(A a);
}

이 인터페이스는 말한다 몇 개의 유형, AB, (라는 함수있다 f소요) A과를 반환 B. 이 인터페이스를 구현하는 경우 AB같은 당신이 기능 제공으로, 당신이 원하는 어떤 종류의 수 있습니다 f전자를 소요하고 후자를 반환합니다. 다음은 인터페이스 구현의 예입니다.

F<Integer, String> intToString = new F<Integer, String>() {
  public String f(int i) {
    return String.valueOf(i);
  }
}

제네릭 이전 에는 키워드를 사용하여 서브 클래 싱 하여 다형성을 달성했습니다 extends. 제네릭을 사용하면 실제로 서브 클래 싱을 없애고 대신 파라 메트릭 다형성을 사용할 수 있습니다. 예를 들어, 모든 유형의 해시 코드를 계산하는 데 사용되는 매개 변수화 된 (일반) 클래스를 고려하십시오. Object.hashCode ()를 재정의하는 대신 다음과 같은 일반 클래스를 사용합니다.

public final class Hash<A> {
  private final F<A, Integer> hashFunction;

  public Hash(final F<A, Integer> f) {
    this.hashFunction = f;
  }

  public int hash(A a) {
    return hashFunction.f(a);
  }
}

이는 상속을 사용하는 것보다 훨씬 더 유연합니다. 부서지기 쉬운 계층 구조를 잠그지 않고도 구성 및 매개 변수 다형성을 사용하는 주제를 유지할 수 있기 때문입니다.

Java의 제네릭은 완벽하지 않습니다. 유형을 통해 추상화 할 수 있지만 예를 들어 유형 생성자를 통해 추상화 할 수는 없습니다. 즉, “모든 유형 T에 대해”라고 말할 수 있지만 “유형 매개 변수 A를 사용하는 모든 유형 T에 대해”라고 말할 수 없습니다.

여기에 Java 제네릭의 이러한 한계에 대한 기사를 썼습니다.

제네릭의 큰 장점 중 하나는 서브 클래 싱을 피할 수 있다는 것입니다. 서브 클래 싱은 확장하기 어려운 부서지기 쉬운 클래스 계층 구조와 전체 계층 구조를 보지 않고는 개별적으로 이해하기 어려운 클래스를 생성하는 경향이 있습니다.

제네릭 전에 Wereas 당신은 같은 클래스를 가질 수 Widget확장 FooWidget, BarWidget그리고 BazWidget, 제네릭 단일 제네릭 클래스 가질 수 Widget<A>소요 Foo, Bar또는 Baz생성자을 제공하기에 Widget<Foo>, Widget<Bar>등을 Widget<Baz>.


답변

Generics는 boxing 및 unboxing의 성능 저하를 방지합니다. 기본적으로 ArrayList 대 List <T>를 살펴보십시오. 둘 다 동일한 핵심 작업을 수행하지만 List <T>는 개체로 /로부터 상자를 만들 필요가 없기 때문에 훨씬 빠릅니다.


답변

Generics의 가장 큰 이점은 코드 재사용입니다. 많은 비즈니스 객체가 있고 동일한 작업을 수행하기 위해 각 엔티티에 대해 매우 유사한 코드를 작성한다고 가정 해 보겠습니다. (IE Linq에서 SQL 작업으로).

제네릭을 사용하면 주어진 기본 클래스에서 상속하는 모든 유형이 주어지면 작동 할 수있는 클래스를 만들거나 다음과 같이 주어진 인터페이스를 구현할 수 있습니다.

public interface IEntity
{

}

public class Employee : IEntity
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int EmployeeID { get; set; }
}

public class Company : IEntity
{
    public string Name { get; set; }
    public string TaxID { get; set }
}

public class DataService<ENTITY, DATACONTEXT>
    where ENTITY : class, IEntity, new()
    where DATACONTEXT : DataContext, new()
{

    public void Create(List<ENTITY> entities)
    {
        using (DATACONTEXT db = new DATACONTEXT())
        {
            Table<ENTITY> table = db.GetTable<ENTITY>();

            foreach (ENTITY entity in entities)
                table.InsertOnSubmit (entity);

            db.SubmitChanges();
        }
    }
}

public class MyTest
{
    public void DoSomething()
    {
        var dataService = new DataService<Employee, MyDataContext>();
        dataService.Create(new Employee { FirstName = "Bob", LastName = "Smith", EmployeeID = 5 });
        var otherDataService = new DataService<Company, MyDataContext>();
            otherDataService.Create(new Company { Name = "ACME", TaxID = "123-111-2233" });

    }
}

위의 DoSomething 메서드에서 다른 유형이 주어 졌을 때 동일한 서비스를 재사용하는 것에 주목하십시오. 정말 우아합니다!

작업에 제네릭을 사용하는 다른 많은 이유가 있습니다. 이것이 제가 가장 좋아하는 것입니다.