수색…


통사론

  • public void SomeMethod <T> () { }
  • public void SomeMethod<T, V>() { }
  • public T SomeMethod<T>(IEnumerable<T> sequence) { ... }
  • public void SomeMethod<T>() where T : new() { }
  • public void SomeMethod<T, V>() where T : new() where V : struct { }
  • public void SomeMethod<T>() where T: IDisposable { }
  • public void SomeMethod<T>() where T: Foo { }
  • public class MyClass<T> { public T Data {get; set; } }

매개 변수

매개 변수 기술
T, V 일반 선언의 자리 표시 자 입력

비고

C #의 제네릭은 런타임까지 계속 지원됩니다. C #으로 작성된 제네릭 형식은 CIL로 컴파일 된 후에도 보존되는 일반적인 의미를 갖습니다.

이것은 C #에서 제네릭 형식을 반영하여 선언 된대로 볼 수 있거나 개체가 제네릭 형식의 인스턴스인지 확인할 수 있다는 것을 의미합니다. 이것은 컴파일 중 제네릭 형식 정보가 제거되는 형식 삭제 와는 대조적입니다. 또한 제네릭에 대한 템플리트 접근 방식과 달리 런타임에서 여러 구체적인 generic 유형이 여러 비 제네릭 유형이되고 원래 generic 유형 정의를 추가로 인스턴스화하는 데 필요한 메타 데이터가 손실됩니다.

그러나 제네릭 형식에 반영 할 때주의해야합니다. 제네릭 형식의 이름은 편집시 변형되고 대괄호 및 형식 매개 변수의 이름을 역 따옴표로 대체 한 다음 제네릭 형식 매개 변수의 개수로 대체됩니다. 따라서 Dictionary<TKey, Tvalue>Dictionary`2 로 변환됩니다.

유형 매개 변수 (클래스)

선언:

class MyGenericClass<T1, T2, T3, ...>
{
    // Do something with the type parameters.
}

초기화 :

var x = new MyGenericClass<int, char, bool>();

사용법 (매개 변수의 유형) :

void AnotherMethod(MyGenericClass<float, byte, char> arg) { ... }

유형 매개 변수 (메소드)

선언:

void MyGenericMethod<T1, T2, T3>(T1 a, T2 b, T3 c)
{
    // Do something with the type parameters.
}

기도:

컴파일러가 형식을 암시 적으로 추론 할 수 있기 때문에 genric 메서드에 형식 인수를 제공 할 필요가 없습니다.

int x =10;
int y =20;
string z = "test";
MyGenericMethod(x,y,z);

그러나 모호성이있는 경우 일반적인 메서드는 arguemnts 형식으로 호출해야합니다.

MyGenericMethod<int, int, string>(x,y,z);

유형 매개 변수 (인터페이스)

선언:

interface IMyGenericInterface<T1, T2, T3, ...> { ... }

사용법 (상속) :

class ClassA<T1, T2, T3> : IMyGenericInterface<T1, T2, T3> { ... }

class ClassB<T1, T2> : IMyGenericInterface<T1, T2, int> { ... }

class ClassC<T1> : IMyGenericInterface<T1, char, int> { ... }

class ClassD : IMyGenericInterface<bool, char, int> { ... }

사용법 (매개 변수의 유형) :

void SomeMethod(IMyGenericInterface<int, char, bool> arg) { ... }

암시적인 형식 유추 (메서드)

정규 인수를 일반 메소드에 전달할 때, 관련 제네릭 유형 인수는 일반적으로 내재적으로 유추 될 수 있습니다. 모든 제네릭 유형을 추론 할 수있는 경우 구문에서이를 지정하는 것은 선택 사항입니다.

다음과 같은 일반적인 방법을 고려하십시오. 그것은 하나의 형식적 매개 변수와 하나의 일반적인 유형 매개 변수를 가지고 있습니다. 그 (것)들 사이 아주 명백한 관계가있다 - 일반적인 유형 매개 변수에 논쟁으로 통과 된 유형은 형식 매개 변수에게 통과 된 논쟁의 컴파일 시간 유형과 동일해야한다.

void M<T>(T obj)
{
}

이 두 호출은 동일합니다.

M<object>(new object());
M(new object());

이 두 가지 호출도 같습니다.

M<string>("");
M("");

그리고이 세가지 부름이 있습니다.

M<object>("");
M((object) "");
M("" as object);

적어도 하나의 형식 인수를 유추 할 수 없다면 모든 인수를 지정해야합니다.

다음과 같은 일반적인 방법을 고려하십시오. 첫 x 째 제네릭 유형 인수는 형식 인수의 유형과 동일합니다. 그러나 두 번째 제네릭 형식 인수에는 그러한 관계가 없습니다. 따라서 컴파일러는이 메서드를 호출 할 때 두 번째 제네릭 형식 인수를 유추 할 수 없습니다.

void X<T1, T2>(T1 obj)
{
}

더 이상 작동하지 않습니다.

X("");

컴파일러는 첫 번째 또는 두 번째 제네릭 매개 변수를 지정하는지 (둘 다 object 로 유효 할 것인가) 확실하지 않으므로이 방법도 작동하지 않습니다.

X<object>("");

우리는 다음과 같이 두 가지 유형을 모두 입력해야합니다.

X<string, object>("");

유형 제약 조건 (클래스 및 인터페이스)

형식 제약 조건은 형식 매개 변수가 특정 인터페이스 나 클래스를 구현하도록 할 수 있습니다.

interface IType;
interface IAnotherType;

// T must be a subtype of IType
interface IGeneric<T>
    where T : IType
{
}

// T must be a subtype of IType
class Generic<T>
    where T : IType
{
}

class NonGeneric
{
    // T must be a subtype of IType
    public void DoSomething<T>(T arg)
        where T : IType
    {
    }
}

// Valid definitions and expressions:
class Type : IType { }
class Sub : IGeneric<Type> { }
class Sub : Generic<Type> { }
new NonGeneric().DoSomething(new Type());

// Invalid definitions and expressions:
class AnotherType : IAnotherType { }
class Sub : IGeneric<AnotherType> { }
class Sub : Generic<AnotherType> { }
new NonGeneric().DoSomething(new AnotherType());

여러 제약 조건에 대한 구문 :

class Generic<T, T1>
    where T : IType 
    where T1 : Base, new()
{
}

유형 제약 조건은 상속과 동일한 방식으로 작동합니다. 따라서 여러 인터페이스를 제네릭 유형의 제약 조건으로 지정할 수 있지만 단 하나의 클래스 만 지정할 수 있습니다.

class A { /* ... */ }
class B { /* ... */ }

interface I1 { }
interface I2 { }

class Generic<T>
    where T : A, I1, I2
{
}

class Generic2<T>
    where T : A, B //Compilation error
{
}

또 다른 규칙은 클래스를 첫 번째 제약 조건으로 추가 한 다음 인터페이스를 추가해야한다는 것입니다.

class Generic<T>
    where T : A, I1
{
}

class Generic2<T>
    where T : I1, A //Compilation error
{
}

특정 일반 인스턴스화가 작동하려면 선언 된 모든 제약 조건이 동시에 만족되어야합니다. 두 개 이상의 대안 세트를 지정하는 방법은 없습니다.

유형 제약 조건 (클래스 및 구조체)

각 제약 조건 class 또는 struct 사용하여 형식 인수가 참조 형식인지 또는 값 형식인지 여부를 지정할 수 있습니다. 이러한 제약 조건이 사용되면 다른 모든 제약 조건 (예 : 부모 유형 또는 new() )을 나열 하기 전에 정의 해야합니다 .

// TRef must be a reference type, the use of Int32, Single, etc. is invalid.
// Interfaces are valid, as they are reference types
class AcceptsRefType<TRef>
    where TRef : class
{
    // TStruct must be a value type.
    public void AcceptStruct<TStruct>()
        where TStruct : struct
    {
    }

    // If multiple constraints are used along with class/struct
    // then the class or struct constraint MUST be specified first
    public void Foo<TComparableClass>()
        where TComparableClass : class, IComparable
    {
    }
}

제약 조건 입력 (new-keyword)

new() 제약 조건을 사용하면 빈 (기본) 생성자를 정의하기 위해 형식 매개 변수를 적용 할 수 있습니다.

class Foo
{
    public Foo () { }
}

class Bar
{
    public Bar (string s) { ... }
}

class Factory<T>
    where T : new()
{
    public T Create()
    {
        return new T();
    }
}

Foo f = new Factory<Foo>().Create(); // Valid.
Bar b = new Factory<Bar>().Create(); // Invalid, Bar does not define a default/empty constructor.

Create() 대한 두 번째 호출은 다음 메시지와 함께 컴파일 시간 오류를 발생시킵니다.

'Bar'는 제네릭 형식 또는 메서드 'Factory'에서 매개 변수 'T'로 사용하기 위해 public 매개 변수가없는 생성자가있는 추상이 아닌 형식이어야합니다.

매개 변수가있는 생성자에는 제약 조건이 없으며 매개 변수없는 생성자 만 지원됩니다.

타입 유추 (클래스)

개발자는 타입 추론 생성자에 대해 작동하지 않는다는 사실에 의해 잡힐 수 있습니다.

class Tuple<T1,T2>
{
   public Tuple(T1 value1, T2 value2)
   {
   }
}

var x = new Tuple(2, "two");              // This WON'T work...
var y = new Tuple<int, string>(2, "two"); // even though the explicit form will.

형식 매개 변수를 명시 적으로 지정하지 않고 인스턴스를 만드는 첫 번째 방법은 다음과 같은 컴파일 시간 오류를 발생시킵니다.

제네릭 형식 'Tuple <T1, T2>'을 사용하려면 2 개의 형식 인수가 필요합니다.

일반적인 해결 방법은 정적 클래스에 도우미 메서드를 추가하는 것입니다.

static class Tuple
{
    public static Tuple<T1, T2> Create<T1, T2>(T1 value1, T2 value2)
    {
         return new Tuple<T1, T2>(value1, value2);
    }
}

var x = Tuple.Create(2, "two");  // This WILL work...

유형 매개 변수 반영

typeof 연산자는 형식 매개 변수에서 작동합니다.

class NameGetter<T>
{
    public string GetTypeName()
    {
        return typeof(T).Name;
    }
}

명시 적 형식 매개 변수

일반 메소드에 대한 유형 매개 변수를 명시 적으로 지정해야하는 경우가 있습니다. 아래 두 경우 모두 컴파일러는 지정된 메서드 매개 변수의 모든 형식 매개 변수를 유추 할 수 없습니다.

한 가지 경우는 매개 변수가없는 경우입니다.

public void SomeMethod<T, V>() 
{
   // No code for simplicity
}

SomeMethod(); // doesn't compile
SomeMethod<int, bool>(); // compiles

두 번째 경우는 형식 매개 변수 중 하나 이상이 메서드 매개 변수의 일부가 아닌 경우입니다.

public K SomeMethod<K, V>(V input)
{
    return default(K);
}

int num1 = SomeMethod(3); // doesn't compile
int num2 = SomeMethod<int>("3"); // doesn't compile
int num3 = SomeMethod<int, string>("3"); // compiles.

인터페이스를 제약 조건 유형으로 사용하는 일반 메소드 사용.

이것은 클래스 Animal에서 Eat 메서드 내에서 generic 형식 TFood를 사용하는 방법의 예입니다.

public interface IFood
{
    void EatenBy(Animal animal);
}

public class Grass: IFood
{
    public void EatenBy(Animal animal)
    {
        Console.WriteLine("Grass was eaten by: {0}", animal.Name);
    }
}

public class Animal
{
    public string Name { get; set; }

    public void Eat<TFood>(TFood food)
        where TFood : IFood
    {
        food.EatenBy(this);
    }
}

public class Carnivore : Animal
{
    public Carnivore()
    {
        Name = "Carnivore";
    }
}

public class Herbivore : Animal, IFood
{
    public Herbivore()
    {
        Name = "Herbivore";
    }
    
    public void EatenBy(Animal animal)
    {
        Console.WriteLine("Herbivore was eaten by: {0}", animal.Name);
    }
}

Eat 메서드는 다음과 같이 호출 할 수 있습니다.

var grass = new Grass();        
var sheep = new Herbivore();
var lion = new Carnivore();
    
sheep.Eat(grass);
//Output: Grass was eaten by: Herbivore

lion.Eat(sheep);
//Output: Herbivore was eaten by: Carnivore

이 경우 전화를 시도 할 경우 :

sheep.Eat(lion);

사자가 인터페이스 IFood를 구현하지 않기 때문에 불가능합니다. 위의 호출을 시도하면 컴파일러 오류가 발생합니다 : "유형 'Carnivore'는 제네릭 형식 또는 메서드 'Animal.Eat (TFood)'에서 형식 매개 변수 'TFood'로 사용할 수 없습니다 ' Carnivore '에서'IFood '까지. "

공분산

IEnumerable<T> 은 다른 IEnumerable<T1> 의 하위 유형 일 때 무엇입니까? TT1 의 부속 유형 일 때. IEnumerableT 매개 변수에서 공변 (covariant) 이며 이는 IEnumerable 의 하위 유형 관계가 T 와 동일한 방향 으로가는 것을 의미합니다.

class Animal { /* ... */ }
class Dog : Animal { /* ... */ }

IEnumerable<Dog> dogs = Enumerable.Empty<Dog>();
IEnumerable<Animal> animals = dogs;  // IEnumerable<Dog> is a subtype of IEnumerable<Animal>
// dogs = animals;  // Compilation error - IEnumerable<Animal> is not a subtype of IEnumerable<Dog>

주어진 유형 매개 변수가있는 공 변 (covariant) 제네릭 유형의 인스턴스는 덜 파생 된 유형 매개 변수를 사용하여 동일한 제네릭 유형으로 암시 적으로 변환 가능합니다.

IEnumerable T 생성 하지만 소비하지 않기 때문에이 관계가 유지됩니다. Dog 를 생성하는 객체는 Animal 을 생성하는 것처럼 사용할 수 있습니다.

공 k 유형 매개 변수는 out 키워드를 g 용하여 선언됩니다. 매개 변수는 출력 으로 g 용되어야하기 때.입니다.

interface IEnumerable<out T> { /* ... */ }

공변 (covariant)으로 선언 된 유형 매개 변수는 입력으로 나타나지 않을 수 있습니다.

interface Bad<out T>
{
    void SetT(T t);  // type error
}

다음은 완전한 예입니다.

using NUnit.Framework;

namespace ToyStore
{
   enum Taste { Bitter, Sweet };

   interface IWidget
   {
      int Weight { get; }
   }

   interface IFactory<out TWidget>
       where TWidget : IWidget
   {
      TWidget Create();
   }

   class Toy : IWidget
   {
      public int Weight { get; set; }
      public Taste Taste { get; set; }
   }

   class ToyFactory : IFactory<Toy>
   {
      public const int StandardWeight = 100;
      public const Taste StandardTaste = Taste.Sweet;

      public Toy Create() { return new Toy { Weight = StandardWeight, Taste = StandardTaste }; }
   }

   [TestFixture]
   public class GivenAToyFactory
   {
      [Test]
      public static void WhenUsingToyFactoryToMakeWidgets()
      {
         var toyFactory = new ToyFactory();

         //// Without out keyword, note the verbose explicit cast:
         // IFactory<IWidget> rustBeltFactory = (IFactory<IWidget>)toyFactory;

         // covariance: concrete being assigned to abstract (shiny and new)
         IFactory<IWidget> widgetFactory = toyFactory;
         IWidget anotherToy = widgetFactory.Create();
         Assert.That(anotherToy.Weight, Is.EqualTo(ToyFactory.StandardWeight)); // abstract contract
         Assert.That(((Toy)anotherToy).Taste, Is.EqualTo(ToyFactory.StandardTaste)); // concrete contract
      }
   }
}

반공립

IComparer<T> 는 다른 IComparer<T1> 의 하위 유형 일 때 무엇입니까? T1T 의 부속 유형 일 때. IComparerT 매개 변수에서 반 변형 이며 IComparer 의 하위 유형 관계는 T 와 반대 방향 으로 향하는 것을 의미합니다.

class Animal { /* ... */ }
class Dog : Animal { /* ... */ }

IComparer<Animal> animalComparer = /* ... */;
IComparer<Dog> dogComparer = animalComparer;  // IComparer<Animal> is a subtype of IComparer<Dog>
// animalComparer = dogComparer;  // Compilation error - IComparer<Dog> is not a subtype of IComparer<Animal>

주어진 타입 매개 변수를 가진 반 차별 제네릭 타입의 인스턴스는 더 파생 된 타입 매개 변수를 가진 동일한 제네릭 타입으로 암시 적으로 변환 가능합니다.

IComparer T 소비 하지만 생성하지 않기 때문에이 관계가 유지됩니다. 두 개의 Animal 비교할 수있는 객체를 사용하여 두 개의 Dog 를 비교할 수 있습니다.

contravariant 유형 매개 변수는 in 키워드를 사용하여 선언됩니다. 왜냐하면 매개 변수는 입력으로 만 사용해야하기 때문입니다.

interface IComparer<in T> { /* ... */ }

반동 문자로 선언 된 유형 매개 변수는 출력으로 나타나지 않을 수 있습니다.

interface Bad<in T>
{
    T GetT();  // type error
}

불변성

IList<T> 는 결코 다른 IList<T1> 의 하위 유형이 아닙니다. IList 는 type 매개 변수에서 불변 합니다.

class Animal { /* ... */ }
class Dog : Animal { /* ... */ }

IList<Dog> dogs = new List<Dog>();
IList<Animal> animals = dogs;  // type error

당신이 목록에 값을 넣고 목록에서 값을 가질 수 있기 때문에 목록에 대한 하위 관계가 없습니다.

IList 가 공변 (covariant)이라면 잘못된 목록의 하위 유형 항목을 주어진 목록에 추가 할 수 있습니다.

IList<Animal> animals = new List<Dog>();  // supposing this were allowed...
animals.Add(new Giraffe());  // ... then this would also be allowed, which is bad!

IList 가 반항 적이라면 주어진 목록에서 잘못된 하위 유형의 값을 추출 할 수 있습니다.

IList<Dog> dogs = new List<Animal> { new Dog(), new Giraffe() };  // if this were allowed...
Dog dog = dogs[1];  // ... then this would be allowed, which is bad!

불변 유형 매개 변수는 inout 키워드를 모두 생략하여 선언됩니다.

interface IList<T> { /* ... */ }

변형 인터페이스

인터페이스에는 변형 유형 매개 변수가있을 수 있습니다.

interface IEnumerable<out T>
{
    // ...
}
interface IComparer<in T>
{
    // ...
}

그러나 수업과 구조는

class BadClass<in T1, out T2>  // not allowed
{
}

struct BadStruct<in T1, out T2>  // not allowed
{
}

제네릭 메소드 선언도하지 마라.

class MyClass
{
    public T Bad<out T, in T1>(T1 t1)  // not allowed
    {
        // ...
    }
}

아래 예제는 동일한 인터페이스에서 여러 분산 선언을 보여줍니다.

interface IFoo<in T1, out T2, T3>
//  T1 : Contravariant type
//  T2 : Covariant type 
//  T3 : Invariant type
{
    // ...
}

IFoo<Animal, Dog, int> foo1 = /* ... */;
IFoo<Dog, Animal, int> foo2 = foo1;  
// IFoo<Animal, Dog, int> is a subtype of IFoo<Dog, Animal, int>

대표 변호인

대리자는 변형 유형 매개 변수를 가질 수 있습니다.

delegate void Action<in T>(T t);    // T is an input
delegate T Func<out T>();           // T is an output
delegate T2 Func<in T1, out T2>();  // T1 is an input, T2 is an output

이것은 (다른 것들 중에서) 방법 D가 방법 B보다 더 유래 된 것으로 간주 될 수 있다고 말하는 Liskov Substitution Principle 에서 나온다 .

  • D는 B와 동등하거나 더 많은 파생 된 반환 유형을가집니다.
  • D는 B보다 같거나 더 일반적인 일반 매개 변수 유형을가집니다.

따라서 다음 할당은 모두 유형 안전합니다.

Func<object, string> original = SomeMethod;
Func<object, object> d1 = original;
Func<string, string> d2 = original;
Func<string, object> d3 = original;

매개 변수 및 반환 값으로 사용되는 변형 유형

공변량 유형이 출력으로 나타나는 경우 포함 유형은 공변입니다. 의 프로듀서 생산 T 의 것은 생산처럼 T 들.

interface IReturnCovariant<out T>
{
    IEnumerable<T> GetTs();
}

contravariant 유형이 출력으로 나타나면 포함하는 유형은 반 변형입니다. 의 소비자 생산 T 의 것은 소모 같다 T 들.

interface IReturnContravariant<in T>
{
    IComparer<T> GetTComparer();
}

공변량 유형이 입력으로 나타나는 경우 포함 유형은 반 변형입니다. 의 생산 소비 T 의 것은 소모 같다 T 들.

interface IAcceptCovariant<in T>
{
    void ProcessTs(IEnumerable<T> ts);
}

contravariant 유형이 입력으로 나타나면 포함 유형은 공변입니다. 의 소비자 소비 T 의 것은 생산처럼 T 들.

interface IAcceptContravariant<out T>
{
    void CompareTs(IComparer<T> tComparer);
}

일반적인 값의 동일성 검사.

제네릭 클래스 또는 메서드의 논리가 제네릭 형식을 갖는 값의 동등성을 검사해야하는 경우 EqualityComparer<TType>.Default 속성을 사용합니다 .

public void Foo<TBar>(TBar arg1, TBar arg2)
{
    var comparer = EqualityComparer<TBar>.Default;
    if (comparer.Equals(arg1,arg2)
    {
        ...
    }
}

TBar 형식이 IEquatale<TBar> 인터페이스를 구현하는지 여부와 IEquatable<TBar>.Equals(TBar other) 메서드를 호출하는지 여부를 확인하기 때문에이 방법은 단순히 Object.Equals() 메서드를 호출하는 것보다 낫습니다. 이렇게하면 값 유형의 boxing / unboxing을 피할 수 있습니다.

일반형 주조

    /// <summary>
    /// Converts a data type to another data type.
    /// </summary>
    public static class Cast
    {
        /// <summary>
        /// Converts input to Type of default value or given as typeparam T
        /// </summary>
        /// <typeparam name="T">typeparam is the type in which value will be returned, it could be any type eg. int, string, bool, decimal etc.</typeparam>
        /// <param name="input">Input that need to be converted to specified type</param>
        /// <param name="defaultValue">defaultValue will be returned in case of value is null or any exception occures</param>
        /// <returns>Input is converted in Type of default value or given as typeparam T and returned</returns>
        public static T To<T>(object input, T defaultValue)
        {
            var result = defaultValue;
            try
            {
                if (input == null || input == DBNull.Value) return result;
                if (typeof (T).IsEnum)
                {
                    result = (T) Enum.ToObject(typeof (T), To(input, Convert.ToInt32(defaultValue)));
                }
                else
                {
                    result = (T) Convert.ChangeType(input, typeof (T));
                }
            }
            catch (Exception ex)
            {
                Tracer.Current.LogException(ex);
            }

            return result;
        }
        
        /// <summary>
        /// Converts input to Type of typeparam T
        /// </summary>
        /// <typeparam name="T">typeparam is the type in which value will be returned, it could be any type eg. int, string, bool, decimal etc.</typeparam>
        /// <param name="input">Input that need to be converted to specified type</param>
        /// <returns>Input is converted in Type of default value or given as typeparam T and returned</returns>
        public static T To<T>(object input)
        {
            return To(input, default(T));
        }

        

    }

용도 :

std.Name = Cast.To<string>(drConnection["Name"]);
std.Age = Cast.To<int>(drConnection["Age"]);
std.IsPassed = Cast.To<bool>(drConnection["IsPassed"]);


// Casting type using default value
//Following both ways are correct
// Way 1 (In following style input is converted into type of default value)
std.Name = Cast.To(drConnection["Name"], "");
std.Marks = Cast.To(drConnection["Marks"], 0);
// Way 2    
std.Name = Cast.To<string>(drConnection["Name"], "");
std.Marks = Cast.To<int>(drConnection["Marks"], 0);

제네릭 형식 캐스팅을 사용하는 구성 판독기

    /// <summary>
    /// Read configuration values from app.config and convert to specified types
    /// </summary>
    public static class ConfigurationReader
    {
        /// <summary>
        /// Get value from AppSettings by key, convert to Type of default value or typeparam T and return
        /// </summary>
        /// <typeparam name="T">typeparam is the type in which value will be returned, it could be any type eg. int, string, bool, decimal etc.</typeparam>
        /// <param name="strKey">key to find value from AppSettings</param>
        /// <param name="defaultValue">defaultValue will be returned in case of value is null or any exception occures</param>
        /// <returns>AppSettings value against key is returned in Type of default value or given as typeparam T</returns>
        public static T GetConfigKeyValue<T>(string strKey, T defaultValue)
        {
            var result = defaultValue;
            try
            {
                if (ConfigurationManager.AppSettings[strKey] != null)
                    result = (T)Convert.ChangeType(ConfigurationManager.AppSettings[strKey], typeof(T));
            }
            catch (Exception ex)
            {
                Tracer.Current.LogException(ex);
            }

            return result;
        }
        /// <summary>
        /// Get value from AppSettings by key, convert to Type of default value or typeparam T and return
        /// </summary>
        /// <typeparam name="T">typeparam is the type in which value will be returned, it could be any type eg. int, string, bool, decimal etc.</typeparam>
        /// <param name="strKey">key to find value from AppSettings</param>
        /// <returns>AppSettings value against key is returned in Type given as typeparam T</returns>
        public static T GetConfigKeyValue<T>(string strKey)
        {
            return GetConfigKeyValue(strKey, default(T));
        }

    }

용도 :

var timeOut = ConfigurationReader.GetConfigKeyValue("RequestTimeout", 2000);
var url = ConfigurationReader.GetConfigKeyValue("URL", "www.someurl.com");
var enabled = ConfigurationReader.GetConfigKeyValue("IsEnabled", false);


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow