문제

내 질문은 C#과 정적 멤버에 액세스하는 방법에 관한 것입니다.글쎄요, 어떻게 설명해야 할지 잘 모르겠습니다. (질문으로는 좋지 않죠?) 몇 가지 샘플 코드를 알려드리겠습니다.

Class test<T>{
     int method1(Obj Parameter1){
         //in here I want to do something which I would explain as
         T.TryParse(Parameter1);

         //my problem is that it does not work ... I get an error.
         //just to explain: if I declare test<int> (with type Integer)
         //I want my sample code to call int.TryParse(). If it were String
         //it should have been String.TryParse()
     }
}

답변해 주셔서 감사합니다. (질문은 다음과 같습니다.오류 없이 이 문제를 해결하려면 어떻게 해야 합니까?)아마도 이것은 당신에게 아주 쉬운 질문일 것입니다!

고마워요, 니클라스


편집하다:답변해 주셔서 감사합니다!

나는 try - 캐치 프레이즈가 가장 우아하다고 생각하지만, VB에 대한 경험을 통해 그것이 정말 실망스러울 수 있다는 것을 알고 있습니다.한번 사용해보니 프로그램 실행하는데 30분 정도 걸렸는데, 나중에는 try-catch를 피해서 계산하는데 2분밖에 안 걸렸어요.

이것이 제가 swich 진술을 최선의 답변으로 선택한 이유입니다.이는 코드를 더 복잡하게 만들지만 반면에 상대적으로 빠르고 상대적으로 읽기 쉽다고 생각합니다.(아직도 좀 더 우아한 방법이 있어야 한다고 생각하지만...어쩌면 내가 배우는 다음 언어일 수도 있습니다 :P )


다른 제안이 있으시면 아직 기다리고 있습니다(참여할 의향이 있음).

도움이 되었습니까?

해결책

이를 수행하는 또 다른 방법은 이번에는 혼합에 대한 반영입니다.

static class Parser
{
    public static bool TryParse<TType>( string str, out TType x )
    {
        // Get the type on that TryParse shall be called
        Type objType = typeof( TType );

        // Enumerate the methods of TType
        foreach( MethodInfo mi in objType.GetMethods() )
        {
            if( mi.Name == "TryParse" )
            {
                // We found a TryParse method, check for the 2-parameter-signature
                ParameterInfo[] pi = mi.GetParameters();
                if( pi.Length == 2 ) // Find TryParse( String, TType )
                {
                    // Build a parameter list for the call
                    object[] paramList = new object[2] { str, default( TType ) };

                    // Invoke the static method
                    object ret = objType.InvokeMember( "TryParse", BindingFlags.InvokeMethod, null, null, paramList );

                    // Get the output value from the parameter list
                    x = (TType)paramList[1];
                    return (bool)ret;
                }
            }
        }

        // Maybe we should throw an exception here, because we were unable to find the TryParse
        // method; this is not just a unable-to-parse error.

        x = default( TType );
        return false;
    }
}

다음 단계는 구현을 시도하는 것입니다.

public static TRet CallStaticMethod<TRet>( object obj, string methodName, params object[] args );

전체 매개변수 유형 일치 등

다른 팁

문제는 TryParse가 인터페이스나 기본 클래스 어디에도 정의되어 있지 않기 때문에 클래스에 전달된 형식에 해당 기능이 있을 것이라고 가정할 수 없다는 것입니다.어떤 식으로든 T를 제한할 수 없다면 이 문제에 많이 직면하게 될 것입니다.

유형 매개변수에 대한 제약

짧은 대답은 할 수 없습니다.

긴 대답입니다. 다음과 같이 속일 수 있습니다.

public class Example
{
    internal static class Support
    {
        private delegate bool GenericParser<T>(string s, out T o);
        private static Dictionary<Type, object> parsers =
            MakeStandardParsers();
        private static Dictionary<Type, object> MakeStandardParsers()
        {
            Dictionary<Type, object> d = new Dictionary<Type, object>();
            // You need to add an entry for every type you want to cope with.
            d[typeof(int)] = new GenericParser<int>(int.TryParse);
            d[typeof(long)] = new GenericParser<long>(long.TryParse);
            d[typeof(float)] = new GenericParser<float>(float.TryParse);
            return d;
        }
        public static bool TryParse<T>(string s, out T result)
        {
            return ((GenericParser<T>)parsers[typeof(T)])(s, out result);
        }
    }
    public class Test<T>
    {
        public static T method1(string s)
        {
            T value;
            bool success = Support.TryParse(s, out value);
            return value;
        }
    }
    public static void Main()
    {
        Console.WriteLine(Test<int>.method1("23"));
        Console.WriteLine(Test<float>.method1("23.4"));
        Console.WriteLine(Test<long>.method1("99999999999999"));
        Console.ReadLine();
    }
}

사용하려는 모든 유형의 TryParse 메서드에 대한 대리자를 보유하는 정적 사전을 만들었습니다.그런 다음 사전을 조회하고 적절한 대리자에게 호출을 전달하는 일반적인 메서드를 작성했습니다.모든 대리자는 다른 유형을 가지므로 이를 개체 참조로 저장하고 검색할 때 적절한 일반 유형으로 다시 캐스팅했습니다.간단한 예를 위해 주어진 유형에 대해 사전에 항목이 있는지 확인하는 것과 같은 오류 검사를 생략했습니다.

특정 클래스나 인터페이스의 멤버에 액세스하려면 Where 키워드를 사용하고 해당 메서드가 있는 인터페이스나 기본 클래스를 지정해야 합니다.

위의 경우 TryParse는 인터페이스나 기본 클래스에서 제공되지 않으므로 위에서 수행하려는 작업은 불가능합니다.Convert.ChangeType과 try/catch 문을 사용하는 것이 가장 좋습니다.

class test<T>
{
    T Method(object P)
    {
       try {
           return (T)Convert.ChangeType(P, typeof(T));
       } catch(Exception e) {
           return null;
       }
    }
}

다음과 같이 하시겠습니까?

Class test<T>
{
     T method1(object Parameter1){

         if( Parameter1 is T ) 
         {
              T value = (T) Parameter1;
             //do something with value
             return value;
         }
         else
         {
             //Parameter1 is not a T
             return default(T); //or throw exception
         }
     }
}

불행하게도 TryParse 패턴은 정적이므로 확인할 수 없습니다. 이는 불행하게도 제네릭에 특히 적합하지 않음을 의미합니다.

찾고 있는 작업을 정확하게 수행하는 유일한 방법은 리플렉션을 사용하여 T에 대한 메서드가 존재하는지 확인하는 것입니다.

또 다른 옵션은 유형을 IConvertible로 제한하여 보내는 개체가 변환 가능한 개체인지 확인하는 것입니다(모든 기본 유형은 IConvertible을 구현함).이렇게 하면 매개변수를 주어진 유형으로 매우 유연하게 변환할 수 있습니다.

Class test<T>
{
    int method1(IConvertible Parameter1){

        IFormatProvider provider = System.Globalization.CultureInfo.CurrentCulture.GetFormat(typeof(T));

        T temp = Parameter1.ToType(typeof(T), provider);
    }
}

원래처럼 '객체' 유형을 사용하여 이에 대한 변형을 수행할 수도 있습니다.

Class test<T>
{
    int method1(object Parameter1){

        if(Parameter1 is IConvertible) {

            IFormatProvider provider = System.Globalization.CultureInfo.CurrentCulture.GetFormat(typeof(T));

            T temp = Parameter1.ToType(typeof(T), provider);

        } else {
           // Do something else
        }
    }
}

알았어 얘들아:모든 물고기에게 감사드립니다.이제 귀하의 답변과 제가 조사한 내용을 바탕으로(특히 일반 유형을 기본 유형으로 제한) 내 해결책을 제시하겠습니다.

Class a<T>{
    private void checkWetherTypeIsOK()
    {
        if (T is int || T is float //|| ... any other types you want to be allowed){
            return true;
        }
        else {
            throw new exception();
        }
    }
    public static a(){
        ccheckWetherTypeIsOK();
    }
}

이는 실제로 해결책은 아니지만 특정 시나리오에서는 좋은 대안이 될 수 있습니다.일반 메서드에 추가 대리자를 전달할 수 있습니다.

무슨 뜻인지 명확히 하기 위해 예를 들어 보겠습니다.T의 인스턴스를 생성해야 하는 일반 팩토리 메서드가 있고 알림이나 추가 초기화를 위해 다른 메서드를 호출하기를 원한다고 가정해 보겠습니다.

다음과 같은 간단한 클래스를 고려해보세요:

public class Example
{
    // ...

    public static void PostInitCallback(Example example)
    {
        // Do something with the object...
    }
}

다음 정적 메서드는 다음과 같습니다.

public static T CreateAndInit<T>() where T : new()
{
    var t = new T();
    // Some initialization code...
    return t;
}

그래서 지금 우리는 다음을 해야 합니다:

var example = CreateAndInit<Example>();
Example.PostInitCallback(example);

그러나 추가 대리자를 사용하도록 메서드를 변경할 수 있습니다.

public delegate void PostInitCallback<T>(T t);
public static T CreateAndInit<T>(PostInitCallback<T> callback) where T : new()
{
    var t = new T();
    // Some initialization code...
    callback(t);
    return t;
}

이제 호출을 다음과 같이 변경할 수 있습니다.

var example = CreateAndInit<Example>(Example.PostInitCallback);

분명히 이것은 매우 특정한 시나리오에서만 유용합니다.그러나 이것은 컴파일 시간 안전성을 확보하고 "해킹"이 관련되지 않으며 코드가 매우 단순하다는 점에서 가장 깨끗한 솔루션입니다.

당신은 아마 그것을 할 수 없습니다.

우선 가능하다면 T에 대한 더 엄격한 경계가 필요하므로 유형 검사기는 T에 대한 가능한 모든 대체 항목에 실제로 TryParse라는 정적 메서드가 있는지 확인할 수 있습니다.

내 이전 게시물을 읽어보고 싶을 수도 있습니다. 일반 유형을 기본 유형으로 제한.이는 제네릭에 전달될 수 있는 유형을 제한하는 데 몇 가지 지침을 제공할 수 있습니다. 유형 구문 분석 분명히 정해진 수의 프리미티브에만 사용할 수 있습니다( 문자열.TryParse 분명히 예외인데, 이는 말이 되지 않습니다).

유형에 대한 더 많은 핸들이 있으면 해당 유형을 구문 분석하는 작업을 수행할 수 있습니다.거기에 약간의 보기 흉한 스위치가 필요할 수도 있습니다(올바른 스위치를 호출하려면 TryParse ) 하지만 원하는 기능을 얻을 수 있다고 생각합니다.

위 내용 중 더 자세한 설명이 필요하시면 문의해주세요 :)

최고의 코드:다음과 같이 T를 ValueType으로 제한합니다.

class test1<T> where T: struct

여기서 "구조체"는 값 유형을 의미합니다.문자열은 값 유형이 아닌 클래스입니다.int, float, Enums는 모두 값 유형입니다.

그런데 컴파일러는 컴파일되지 않는 다음 예제와 같이 '유형 매개변수'에 대한 정적 메소드 호출이나 정적 멤버 액세스를 허용하지 않습니다.

class MyStatic { public static int MyValue=0; }
class Test<T> where T: MyStatic
{
    public void TheTest() { T.MyValue++; }
}

=> 오류 1 'T'는 '유형 매개변수'이며, 이는 주어진 컨텍스트에서 유효하지 않습니다.

SL.

그것은 정적이 작동하는 방식이 아닙니다.정적이 여러 유형에 분산되어 있더라도 일종의 전역 클래스로 생각해야 합니다.제가 권장하는 것은 필요한 정적 메서드에 액세스할 수 있는 T 인스턴스 내부의 속성으로 만드는 것입니다.

또한 T는 실제 인스턴스이며 다른 인스턴스와 마찬가지로 인스턴스화된 값을 통해 해당 유형의 정적 변수에 액세스할 수 없습니다.다음은 수행할 작업의 예입니다.

class a {
    static StaticMethod1 ()
    virtual Method1 ()
}

class b : a {
    override Method1 () return StaticMethod1()
}

class c : a {
    override Method1 () return "XYZ"
}

class generic<T> 
    where T : a {
    void DoSomething () T.Method1()
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top