Вопрос

Мой вопрос касается С# и доступа к статическим участникам...Ну, я действительно не знаю, как это объяснить (что плохо для вопроса, не так ли?) Я просто приведу вам пример кода:

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()
     }
}

Так что спасибо, ребята, за ваши ответы (Кстати, вопрос такой:как бы мне решить эту проблему, не получив ошибки).Вероятно, это довольно простой вопрос для вас!

Спасибо, Никлас


Редактировать:Спасибо всем за ваши ответы!

Хотя я считаю, что фраза «попробуй - поймай» является самой элегантной, по своему опыту работы с vb я знаю, что это действительно может быть обломом.Я использовал его один раз, и запуск программы занял около 30 минут, а позже на вычисление ушло всего 2 минуты только потому, что я избегал попытки-поймай.

Вот почему я выбрал утверждение о переключении как лучший ответ.Это усложняет код, но, с другой стороны, я думаю, что он относительно быстрый и относительно легко читаемый.(Хотя я все же думаю, что должен быть более элегантный способ...возможно, на следующем языке, который я изучаю :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

«Структура» здесь означает тип значения.String — это класс, а не тип значения.int, float, Enum — все это типы значений.

кстати, компилятор не принимает вызов статических методов или доступ к статическим членам для «параметров типа», как в следующем примере, который не будет компилироваться :(

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

=> Ошибка 1. «T» — это «параметр типа», который недопустим в данном контексте.

СЛ.

Статика так не работает.Вы должны думать о статике как о чем-то вроде глобального класса, даже если они распределены по целому ряду типов.Я рекомендую сделать это свойством внутри экземпляра 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