Сведения о вопросе

FromRussia

02:35, 8th August, 2020

Теги

c#   generics   static   methods   data-access    

Дженерики в c# и доступ к статическим членам Т

Просмотров: 507   Ответов: 12

Мой вопрос касается 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()
     }
}

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

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


Edit: спасибо всем за ваши ответы!

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

Вот почему я выбрал утверждение swich в качестве лучшего ответа. Это делает код более сложным, но с другой стороны, я думаю, что он будет относительно быстрым и относительно легким для чтения. (Хотя я все еще думаю, что должен быть более элегантный способ ... может быть, на следующем языке, который я изучаю: P )


Хотя, если у вас есть какое-то другое предложение, я все еще жду (и готов принять участие)



  Сведения об ответе

SEEYOU

17:08, 9th August, 2020

Проблема заключается в том, что TryParse нигде не определен в интерфейсе или базовом классе, поэтому вы не можете сделать предположение, что тип, переданный в ваш класс, будет иметь эту функцию. Если вы не можете каким-то образом сдерживать T, вы будете часто сталкиваться с этим.

Ограничения на параметры типа


  Сведения об ответе

SILA

10:56, 12th August, 2020

Чтобы получить доступ к члену определенного класса или интерфейса, необходимо использовать ключевое слово 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;
       }
    }
}


  Сведения об ответе

SKY

12:51, 15th August, 2020

Короткий ответ, вы не можете.

Длинный ответ, вы можете обмануть:

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 каждого типа, который я мог бы использовать. Затем я написал универсальный метод для поиска словаря и передачи вызова соответствующему делегату. Поскольку каждый делегат имеет свой тип, я просто сохраняю их как ссылки на объекты и возвращаю их обратно к соответствующему универсальному типу при их извлечении. Обратите внимание, что ради простого примера я опустил проверку ошибок, например, чтобы проверить, есть ли у нас запись в словаре для данного типа.


  Сведения об ответе

ASSembler

05:31, 20th August, 2020

Еще один способ сделать это, на этот раз некоторое отражение в миксе:

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 );

С полным соответствием типа параметра etc.


  Сведения об ответе

darknet

23:27, 15th August, 2020

Неужели ты собираешься сделать что-то подобное:

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


  Сведения об ответе

lesha

16:03, 27th August, 2020

Единственный способ сделать именно то, что вы ищете, - это использовать отражение, чтобы проверить, существует ли метод для 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);
    }
}

Вы также можете сделать вариацию на эту тему, используя вместо этого тип 'object', как это было изначально.

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


  Сведения об ответе

baggs

12:40, 21st August, 2020

ОК, ребята: спасибо за всю рыбу. Теперь с вашими ответами и моими исследованиями (особенно со статьей об ограничении родовых типов примитивами ) я представлю вам свое решение.

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


  Сведения об ответе

DINO

15:46, 20th August, 2020

Это не совсем решение, но в некоторых сценариях это может быть хорошей альтернативой: мы можем передать дополнительный делегат универсальному методу.

Чтобы пояснить, что я имею в виду, давайте возьмем пример. Допустим, у нас есть какой-то универсальный фабричный метод, который должен создать экземпляр 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);

Очевидно, что это полезно только в очень специфических сценариях. Но это самое чистое решение в том смысле, что мы получаем безопасность во время компиляции, здесь нет "hacking", и код очень прост.


  Сведения об ответе

padenie

00:08, 4th August, 2020

Лучший код: ограничьте T до ValueType таким образом:

class test1<T> where T: struct

A "struct" здесь означает тип значения. String - это класс, а не тип значения. int, float, Enums - это все типы значений.

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

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

=> Ошибка 1 'T' - это 'type parameter', которая недопустима в данном контексте

SL.


  Сведения об ответе

+-*/

07:14, 17th August, 2020

Ты, наверное, не сможешь этого сделать.

Прежде всего, если бы это было возможно, Вам понадобилась бы более жесткая привязка к T, чтобы typechecker мог быть уверен, что все возможные замены для T на самом деле имеют статический метод с именем TryParse.


  Сведения об ответе

dump

14:23, 23rd August, 2020

Возможно, вы захотите прочитать мой предыдущий пост об ограничении универсальных типов примитивами . Это может дать вам некоторые указания в ограничении типа, который может быть передан универсальному (так как TypeParse , очевидно, доступен только для заданного числа примитивов ( string.TryParse , очевидно, является исключением, что не имеет смысла).

Как только у вас будет больше дескриптора для типа, вы можете работать над попыткой его проанализировать. Вам может понадобиться немного уродливый переключатель там (чтобы вызвать правильный TryParse ), но я думаю, что вы можете достичь желаемой функциональности.

Если вам нужно, чтобы я объяснил что-либо из вышеперечисленного дальше, то, пожалуйста, спросите :)


  Сведения об ответе

baggs

01:41, 28th August, 2020

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться