Отражение и Параметры в C#

Я пишу приложение, которое выполняет "вещи" к расписанию.

Причем идея состоит в том, что база данных содержит блок, информацию о методе и также значения параметров. Таймер придет, отразит метод, который будет выполнен, добавит параметры и затем выполнит метод.

Все прекрасно за исключением параметров.

Так, позволяет, говорят, что метод принимает ПЕРЕЧИСЛЕНИЕ CustomerType, где CustomerType имеет два значения CustomerType. Ведущее устройство и CustomerType. Партнер.

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

Мы хотим выполнить Метод "X" и передачу в параметре "CustomerType. Ведущее устройство". В базе данных будет varchar запись "CustomerType. Ведущее устройство".

Как я преобразовываю строку "CustomerType. Ведущее устройство" в тип CustomerType со значением "Ведущего устройства" в общем?

Заранее спасибо,

Jim

5
задан Mike Two 9 March 2010 в 06:53
поделиться

5 ответов

Я думаю, у вас есть 2 основных варианта:

  1. Сохраните имя типа вместе со значением параметра и используйте его для приведения объектов с помощью Type .GetType (строка) для разрешения рассматриваемого типа.
  2. Стандартизируйте все вызываемые методы таким образом, чтобы они принимали массив строк, и ожидайте, что методы будут выполнять любое необходимое приведение.

Я знаю, что вы заявили, что не используете вариант 1, но он может помочь с точки зрения вызова функций.

Вариант 2 - это гораздо более «общий» способ справиться с ситуацией, предполагающий, что все значения могут быть представлены и преобразованы / преобразованы из строк в соответствующий тип. Конечно, это помогает только в том случае, если вы действительно контролируете определение вызываемых методов.

1
ответ дан 15 December 2019 в 06:24
поделиться

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

Я думаю, вы не можете / не можете использовать здесь «дженерики». Вы не знаете тип заранее, и, поскольку вам нужно будет создать тип, нет необходимости использовать универсальную реализацию, потому что MethodBase.Invoke принимает массив Object.

Этот код предполагает, что вы создаете экземпляр цели из поля базы данных. Если нет, просто отрегулируйте соответственно.

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

ПРИМЕЧАНИЕ : существует много-много сценариев, в которых этот простой исполнитель не будет работать. Вам нужно будет убедиться, что вы разрабатываете свои динамические методы для взаимодействия с любой стратегией, которую вы в конечном итоге решите использовать.

using System;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Reflection;
using NUnit.Framework;

namespace DynamicMethodInvocation
{

    [TestFixture]
    public class Tests
    {
        [Test]
        public void Test()
        {
            // from your database 
            string assemblyQualifiedTypeName = "DynamicMethodInvocation.TestType, DynamicMethodInvocation";
            string methodName = "DoSomething";

            // this is how you would get the strings to put in your database
            string enumString = Executor.ConvertToString(typeof(AttributeTargets), AttributeTargets.Assembly);
            string colorString = Executor.ConvertToString(typeof(Color), Color.Red);
            string stringString = "Hmm... String?";

            object result = Executor.ExecuteMethod(assemblyQualifiedTypeName, methodName,
                                                   new[] { enumString, colorString, stringString });

            Assert.IsInstanceOf<bool>(result);
            Assert.IsTrue((bool)result);
        }
    }


    public class TestType
    {
        public bool DoSomething(AttributeTargets @enum, Color color, string @string)
        {
            return true;
        }
    }

    public class Executor
    {
        public static object ExecuteMethod(string assemblyQualifiedTypeName, string methodName,
                                           string[] parameterValueStrings)
        {
            Type targetType = Type.GetType(assemblyQualifiedTypeName);
            MethodBase method = targetType.GetMethod(methodName);

            ParameterInfo[] pInfo = method.GetParameters();
            var parameterValues = new object[parameterValueStrings.Length];

            for (int i = 0; i < pInfo.Length; i++)
            {
                parameterValues[i] = ConvertFromString(pInfo[i].ParameterType, parameterValueStrings[i]);
            }

            // assumes you are instantiating the target from db and that it has a parameterless constructor
            // otherwise, if the target is already known to you and instantiated, just use it...

            return method.Invoke(Activator.CreateInstance(targetType), parameterValues);
        }


        public static string ConvertToString(Type type, object val)
        {
            if (val is string)
            {
                return (string) val;
            }
            TypeConverter tc = TypeDescriptor.GetConverter(type);
            if (tc == null)
            {
                throw new Exception(type.Name + " is not convertable to string");
            }
            return tc.ConvertToString(null, CultureInfo.InvariantCulture, val);
        }

        public static object ConvertFromString(Type type, string val)
        {
            TypeConverter tc = TypeDescriptor.GetConverter(type);
            if (tc == null)
            {
                throw new Exception(type.Name + " is not convertable.");
            }
            if (!tc.IsValid(val))
            {
                throw new Exception(type.Name + " is not convertable from " + val);
            }

            return tc.ConvertFrom(null, CultureInfo.InvariantCulture, val);
        }
    }

}
2
ответ дан 15 December 2019 в 06:24
поделиться

Если вы используете .NET 4, вы можете сделать следующее.

var result = default(CustomerType);
if (!Enum.TryParse("Master", out result))
{
    // handle error
}
-1
ответ дан 15 December 2019 в 06:24
поделиться

Ниже приведен полезный метод расширения, который я использую в .NET 3.5.

Если доступен этот метод расширения, ваш код может выглядеть следующим образом:

var valueInDb = GetStringFromDb().Replace("CustomerType.", string.Empty);
var value = valueInDb.ToEnum(CustomerType.Associate);

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

Вот метод расширения: (эта версия также выполняет частичные совпадения, поэтому даже «M» будет работать нормально!)

public static T ToEnum<T>(this string input, T defaultValue)
    {
      var enumType = typeof (T);
      if (!enumType.IsEnum)
      {
        throw new ArgumentException(enumType + " is not an enumeration.");
      }

      // abort if no value given
      if (string.IsNullOrEmpty(input))
      {
        return defaultValue;
      }

      // see if the text is valid for this enumeration (case sensitive)
      var names = Enum.GetNames(enumType);

      if (Array.IndexOf(names, input) != -1)
      {
        // case insensitive...
        return (T) Enum.Parse(enumType, input, true);
      }

      // do partial matching...
      var match = names.Where(name => name.StartsWith(input, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
      if(match != null)
      {
        return (T) Enum.Parse(enumType, match);
      }

      // didn't find one
      return defaultValue;
    }
1
ответ дан 15 December 2019 в 06:24
поделиться

Я до сих пор не совсем понимаю ваш вопрос ... однако вы говорите: «Все в порядке, кроме параметров».

Я Предположим, что «CustomerType» - это имя свойства вашего объекта, а «Master» - это строковое значение, которое вы хотите поместить в это свойство.

Вот (еще один) метод расширения, который может помочь.

Когда у вас есть новый объект, а также значение и имя свойства из поля базы данных, вы можете использовать это:

// string newValue = "Master";
// string propertyName = "CustomerType"; 

myNewObject.SetPropertyValue(propertyName, newValue)

Метод:

/// <summary>Set the value of this property, as an object.</summary>
public static void SetPropertyValue(this object obj, 
                                    string propertyName, 
                                    object objValue)
{
  const BindingFlags attr = BindingFlags.Public | BindingFlags.Instance;
  var type = obj.GetType();

  var property = type.GetProperty(propertyName, attr);
  if(property == null) return;

  var propertyType = property.PropertyType;
  if (propertyType.IsValueType && objValue == null)
  {
    // This works for most value types, but not custom ones
    objValue = 0;
  }

  // need to change some types... e.g. value may come in as a string...
  var realValue = Convert.ChangeType(objValue, propertyType);

  property.SetValue(obj, realValue, null);
}
0
ответ дан 15 December 2019 в 06:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: