Как мне перейти от типа к методу TryParse? - PullRequest
8 голосов
/ 24 января 2009

Моя конкретная проблема:

У меня есть строка, которая указывает тип записи в классе конфигурации

Config.numberType = "System.Foo";

, где Foo - это тип, подобный Decimal или Double

Я использую Type.GetType(Config.numberType), чтобы вернуть соответствующий тип.

Как мне перейти от этого типа к возможности использовать, System.Foo.TryParse()?

Некоторые дополнительные вопросы

  • TryParse() можно получить с System.Foo.TryParse(), а также с foo.TryParse(). Означает ли это, что foo - это какой-то класс в C #? Мне кажется странным, что int, double и т. Д. На самом деле не просто ключевые слова-модификаторы.
  • Как вы можете объявлять переменные при таких обстоятельствах? - var не может использоваться универсально, кажется, т.е. только в локальной области и т. Д.

Ответы [ 5 ]

20 голосов
/ 25 января 2009

Как уже говорили многие, прямого маршрута нет. Я ожидаю, что один из вариантов закрытия - TypeConverter:

    Type type = typeof(double);
    string text = "123.45";

    object value = TypeDescriptor.GetConverter(type)
        .ConvertFromInvariantString(text);

Конечно, вам может понадобиться try / catch для обработки исключений. Такова жизнь.

7 голосов
/ 24 января 2009

Как мне добраться от этого типа до бытия? в состоянии использовать, System.Foo.TryParse ()?

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

TryParse () доступен из System.Foo.TryParse (), а также foo.TryParse (). Означает ли это, что Фу какой-то класс в C #?

int, double и т. Д. Являются псевдонимами для System.Int32, System.Double и т. Д. - они являются частью языка C #, который без них будет неудобно многословным.

Как вы можете объявить переменные в эти обстоятельства?

Не зная тип во время компиляции, вы будете вынуждены объявить и работать с вашими данными как object / System.Object. C # 4.0 представит реальные динамические типы, которые позаботятся о некоторых утомительных работах по отражению для вас, но сейчас вы застряли, делая это вручную. Обратите внимание, что если вы используете System.Convert в методе с параметризованным аргументом типа и возвращаете или TryParse(), используя такую ​​технику, как , на которую ссылается Себастьян Седлак , вы можете легко получить возможность писать клиентский код, который работает со статическими типами ... Пока они совпадают или могут быть преобразованы в типы, которые вы анализируете.

5 голосов
/ 25 января 2009

РЕДАКТИРОВАНИЕ: Я удалил общую реализацию и очистил этот ответ, чтобы лучше соответствовать первоначально заявленной проблеме.

ПРИМЕЧАНИЕ. Ответ Марка Гравелла, вероятно, является наиболее кратким, если вы просто хотите, чтобы проанализированное значение было задано типом. Ответ ниже показывает, как получить метод (т. Е. Объект MethodInfo и как его вызвать).

Следующее должно работать, по крайней мере, для типов, которые реализуют public static bool TryParse (строка, значение T):

public static class Parsing
{
    static MethodInfo findTryParseMethod(Type type)
    {
        //find member of type with signature 'static public bool TryParse(string, out T)'
        BindingFlags access = BindingFlags.Static | BindingFlags.Public;
        MemberInfo[] candidates = type.FindMembers(
            MemberTypes.Method,
            access,
            delegate(MemberInfo m, object o_ignored)
            {
                MethodInfo method = (MethodInfo)m;
                if (method.Name != "TryParse") return false;
                if (method.ReturnParameter.ParameterType != typeof(bool)) return false;
                ParameterInfo[] parms = method.GetParameters();
                if (parms.Length != 2) return false;
                if (parms[0].ParameterType != typeof(string)) return false;
                if (parms[1].ParameterType != type.MakeByRefType()) return false;
                if (!parms[1].IsOut) return false;

                return true;

            }, null);

        if (candidates.Length > 1)
        {
            //change this to your favorite exception or use an assertion
            throw new System.Exception(String.Format(
                "Found more than one method with signature 'public static bool TryParse(string, out {0})' in type {0}.",
                type));
        }
        if (candidates.Length == 0)
        {
            //This type does not contain a TryParse method - replace this by your error handling of choice
            throw new System.Exception(String.Format(
                "Found no method with signature 'public static bool TryParse(string, out {0})' in type {0}.",
                type));
        }
        return (MethodInfo)candidates[0];
    }

    public static bool TryParse(Type t, string s, out object val)
    {
        MethodInfo method = findTryParseMethod(t); //can also cache 'method' in a Dictionary<Type, MethodInfo> if desired
        object[] oArgs = new object[] { s, null };
        bool bRes = (bool)method.Invoke(null, oArgs);
        val = oArgs[1];
        return bRes;
    }

    //if you want to use TryParse in a generic syntax:
    public static bool TryParseGeneric<T>(string s, out T val)
    {
        object oVal;
        bool bRes = TryParse(typeof(T), s, out oVal);
        val = (T)oVal;
        return bRes;
    }
}

Используйте следующий тестовый код:

        public bool test()
    {
        try
        {
            object oVal;
            bool b = Parsing.TryParse(typeof(int), "123", out oVal);
            if (!b) return false;
            int x = (int)oVal;
            if (x!= 123) return false;
        }
        catch (System.Exception)
        {
            return false;
        }

        try
        {
            int x;
            bool b = Parsing.TryParseGeneric<int>("123", out x);
            if (!b) return false;
            if (x != 123) return false;
        }
        catch (System.Exception)
        {
            return false;
        }


        try
        {
            object oVal;
            bool b = Parsing.TryParse(typeof(string), "123", out oVal);
            //should throw an exception (//no method String.TryParse(string s, out string val)
            return false;
        }
        catch (System.Exception)
        {
            //should throw an exception
        }

        return true;
    }
}

И используйте это в вашем случае:

    //input: string s, Config
Type tNum = Type.GetType(Config.numberType);    
object oVal;
bool ok = Parsing.TryParse(tNum, s, out oVal);
//oVal is now of type tNum and its value is properly defined if ok == true

Об использовании var: у вас может быть неправильное представление о том, что делает var: это не «вариантный» тип (объект типа уже используется для этого), но он перемещает синтаксис объявления для типа в правую часть присваивания , Следующие объявления эквивалентны:

var i = 1;  //the compiler infers the type from the assignment, type of i is int.
int i = 1;  //type of i is int via declaration

Основное использование var позволяет создавать анонимных типов :

var anon = new { Name = "abc", X = 123 };
2 голосов
/ 03 февраля 2010

И еще хорошая ссылка на эту проблему . Исходный код на этом сайте очень плохо отформатирован, поэтому я разместил его здесь. Надеюсь, автор не судится со мной.

public static T Parse<T>(string s)
{
    Type t = typeof(T);
    // Attempt to execute the Parse method on the type if it exists. 
    MethodInfo parse = t.GetMethod("Parse", new Type[] { typeof(string) });

    if (parse != null)
    {
        try
        {
            return (T)parse.Invoke(null, new object[] { s });
        }
        catch (Exception ex)
        {
            throw ex.InnerException;
        }
    }
    else
    {
        throw new MethodAccessException(String.Format("The Parse method does not exist for type {0}.", t.Name));
    }
}

public static bool TryParse<T>(string s, out T result)
{
    return TryParse<T>(s, false, out result);
}

public static bool TryParse<T>(string s, bool throwException, out T result)
{
    result = default(T);
    Type t = typeof(T);
    T type = default(T);

    // Look for the TryParse method on the type. 
    MethodInfo tryParse = t.GetMethod("TryParse", new Type[] { typeof(string), Type.GetType(t.FullName + "&") });
    if (tryParse != null)
    {
        // Try parse exists. Call it. 
        Object[] ps = new Object[2];
        ps[0] = s;

        bool isSuccess = (bool)tryParse.Invoke(type, ps);

        if (isSuccess)
            result = (T)ps[1];

        return isSuccess;
    }
    else
    {
        // TryParse does not exist. Look for a Parse method. 
        try
        {
            result = Parse<T>(s);
            return true;
        }
        catch
        {
            if (throwException)
                throw;

            return false;
        }
    }
}
1 голос
/ 24 января 2009
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...