이것을 읽으 면서 메소드가 일반적인 메소드로 만들어 여러 유형의 매개 변수를 허용 할 수 있다는 것을 알게되었습니다. 이 예에서 다음 코드는 유형 제약 조건과 함께 사용되어 “U”가입니다 IEnumerable<T>
.
public T DoSomething<U, T>(U arg) where U : IEnumerable<T>
{
return arg.First();
}
다음과 같은 여러 유형 제약 조건을 추가 할 수있는 코드가 더 있습니다.
public void test<T>(string a, T arg) where T: ParentClass, ChildClass
{
//do something
}
그러나이 코드 arg
는 ParentClass
및 유형이어야합니다 ChildClass
. 내가하고 싶은 것은 arg가 유형 ParentClass
이거나 ChildClass
다음과 같은 방식 일 수 있다는 것입니다.
public void test<T>(string a, T arg) where T: string OR Exception
{
//do something
}
당신의 도움은 항상 감사합니다!
답변
불가능합니다. 그러나 특정 유형에 대한 과부하를 정의 할 수 있습니다.
public void test(string a, string arg);
public void test(string a, Exception arg);
그것들이 제네릭 클래스의 일부라면, 제네릭 버전의 메소드보다 선호됩니다.
답변
Botz의 대답은 100 % 정확합니다. 간단한 설명은 다음과 같습니다.
메소드를 작성하거나 (일반적이든 아니든) 메소드가 취하는 매개 변수 유형을 선언하면 계약을 정의하는 것입니다.
Type T가 수행하는 방법을 알고있는 일련의 작업을 수행하는 방법을 알고있는 객체를 제공하면 ‘a’: 선언 한 유형의 반환 값 또는 ‘b’: 사용하는 일종의 동작을 전달할 수 있습니다 그 유형.
한 번에 두 개 이상의 유형을 제공하려고하거나 (또는 or를 가짐) 계약이 퍼지되는 둘 이상의 유형이 될 수있는 값을 리턴하려고 시도하는 경우 :
줄넘기를하는 방법을 알고 있거나 pi를 15 자리로 계산하는 방법을 알고있는 물체를 주면 낚시를하거나 콘크리트를 섞을 수있는 물체를 돌려줍니다.
문제는 당신이 방법에 들어갈 때 그들이 당신에게 또는를 주 었는지 전혀 모른다 IJumpRope
는 것 PiFactory
입니다. 또한, 계속 진행하고 (매직 컴파일하기 위해 가정했다고 가정 할 때) 메소드를 사용할 때 실제로 Fisher
또는AbstractConcreteMixer
. 기본적으로 그것은 모든 것을 더 혼란스럽게 만듭니다.
문제에 대한 해결책은 다음 두 가지 가능성 중 하나입니다.
-
각각의 가능한 변환, 동작 등을 정의하는 둘 이상의 방법을 정의하십시오. 보츠의 대답입니다. 프로그래밍 세계에서는이를 메서드 오버로딩이라고합니다.
-
메소드에 필요한 모든 작업을 수행하는 방법을 알고 기본 클래스 또는 인터페이스를 정의하고 하나의 메소드 만 해당 유형을 갖도록하십시오 . 구현에 매핑하는 방법을 정의 하기 위해 a
string
와Exception
작은 클래스를 래핑하는 것이 포함될 수 있지만 모든 것이 매우 명확하고 읽기 쉽습니다. 나는 4 년 후에 와서 코드를 읽고 무슨 일이 일어나고 있는지 쉽게 이해할 수있었습니다.
선택하는 것은 선택 1과 2가 얼마나 복잡한 지와 얼마나 확장 가능한지에 달려 있습니다.
따라서 특정 상황에 대해 예외에서 메시지 또는 무언가를 꺼내고 있다고 상상할 것입니다.
public interface IHasMessage
{
string GetMessage();
}
public void test(string a, IHasMessage arg)
{
//Use message
}
이제 a string
와 a Exception
를 IHasMessage로 변환하는 메소드 만 있으면됩니다. 아주 쉽게.
답변
ChildClass가 ParentClass에서 파생 된 것을 의미하는 경우 ParentClass와 ChildClass를 모두 허용하도록 다음을 작성할 수 있습니다.
public void test<T>(string a, T arg) where T: ParentClass
{
//do something
}
반면에 상속 관계가없는 두 가지 유형을 사용하려면 동일한 인터페이스를 구현하는 유형을 고려해야합니다.
public interface ICommonInterface
{
string SomeCommonProperty { get; set; }
}
public class AA : ICommonInterface
{
public string SomeCommonProperty
{
get;set;
}
}
public class BB : ICommonInterface
{
public string SomeCommonProperty
{
get;
set;
}
}
그런 다음 일반 함수를 다음과 같이 작성할 수 있습니다.
public void Test<T>(string a, T arg) where T : ICommonInterface
{
//do something
}
답변
이 질문만큼 오래된 것은 위의 설명에서 여전히 임의의 공감대를 얻습니다. 설명은 여전히 완벽하게 잘 유지되지만, 나는 유니온 타입 (C #에서 직접 지원하지 않는 질문에 대한 강력한 형식의 답변) 대신 나에게 도움이되는 유형으로 두 번째로 대답 할 것입니다. ).
using System;
using System.Diagnostics;
namespace Union {
[DebuggerDisplay("{currType}: {ToString()}")]
public struct Either<TP, TA> {
enum CurrType {
Neither = 0,
Primary,
Alternate,
}
private readonly CurrType currType;
private readonly TP primary;
private readonly TA alternate;
public bool IsNeither => currType == CurrType.Primary;
public bool IsPrimary => currType == CurrType.Primary;
public bool IsAlternate => currType == CurrType.Alternate;
public static implicit operator Either<TP, TA>(TP val) => new Either<TP, TA>(val);
public static implicit operator Either<TP, TA>(TA val) => new Either<TP, TA>(val);
public static implicit operator TP(Either<TP, TA> @this) => @this.Primary;
public static implicit operator TA(Either<TP, TA> @this) => @this.Alternate;
public override string ToString() {
string description = IsNeither ? "" :
$": {(IsPrimary ? typeof(TP).Name : typeof(TA).Name)}";
return $"{currType.ToString("")}{description}";
}
public Either(TP val) {
currType = CurrType.Primary;
primary = val;
alternate = default(TA);
}
public Either(TA val) {
currType = CurrType.Alternate;
alternate = val;
primary = default(TP);
}
public TP Primary {
get {
Validate(CurrType.Primary);
return primary;
}
}
public TA Alternate {
get {
Validate(CurrType.Alternate);
return alternate;
}
}
private void Validate(CurrType desiredType) {
if (desiredType != currType) {
throw new InvalidOperationException($"Attempting to get {desiredType} when {currType} is set");
}
}
}
}
상기 클래스가 될 수있는 형식 나타내는 하나 TP 또는 TA를. 당신은 그것을 그대로 사용할 수 있습니다 (유형은 원래의 대답으로 되돌아갑니다) :
// ...
public static Either<FishingBot, ConcreteMixer> DemoFunc(Either<JumpRope, PiCalculator> arg) {
if (arg.IsPrimary) {
return new FishingBot(arg.Primary);
}
return new ConcreteMixer(arg.Secondary);
}
// elsewhere:
var fishBotOrConcreteMixer = DemoFunc(new JumpRope());
var fishBotOrConcreteMixer = DemoFunc(new PiCalculator());
중요 사항 :
- 확인하지 않으면 런타임 오류가 발생합니다
IsPrimary
먼저 . IsNeither
IsPrimary
또는 중 하나를 확인할 수 있습니다IsAlternate
.- 당신은 통해 값에 액세스 할 수 있습니다
Primary
및Alternate
- TP / TA와 둘 사이에 암시 적 변환기가있어 값이나
Either
예상되는 위치 를 전달할 수 있습니다 . 당신 이 통과하면Either
곳TA
이상이TP
예상된다하지만,이Either
값의 잘못된 유형을 포함하면 런타임 오류가 발생합니다.
나는 일반적으로 메소드가 결과 또는 오류를 반환하기를 원할 때 이것을 사용합니다. 실제로 해당 스타일 코드를 정리합니다. 나는 또한 메소드 오버로드를 대신하여 이것을 거의 사용 하지 않는 경우도있다 . 실제로 이것은 과부하를 대체하기에 매우 열악합니다.
답변
