2010-03-03 5 views
5

"일정"에 "물건"을 실행하는 응용 프로그램을 작성하고 있습니다.C에서의 반사 및 매개 변수 #

아이디어는베이스 어셈블리에있어서 정보와 같은 매개 변수 값을 포함하는 것이 되. 타이머가 실행되고, 실행될 메소드를 반영하고, 매개 변수를 추가 한 다음 메소드를 실행합니다.

매개 변수를 제외한 모든 항목이 정상입니다.

그래서,이 방법은 CustomerType이 CustomerType.Master 및 CustomerType.Associate 두 값이 CustomerType의 ENUM을 받아 말할 수 있습니다.

편집 나는 전달하기됩니다 매개 변수의 유형을 알고하지 않습니다. ENUM은 예를 들어 편집의 END

우리는 방법 "X를 실행하려면 사용 매개 변수 "CustomerType.Master"를 전달하십시오. 데이터베이스에는 "CustomerType.Master"라는 varchar 항목이 있습니다.

어떻게 일반적으로 "마스터"의 값을 CustomerType의 유형에 문자열 "CustomerType.Master"를 변환합니까? 사전에

감사합니다,

+0

내가 생각 '일반적으로'대신 '동적으로'. 당신은 요구 사항이 설명 문자열에서 매개 변수 값을 동적으로 생성해야하는 필요성을 설명 할 때 그 사용법에 대한 많은 답변을 얻었습니다. 내가 너를 제대로 이해하고 있니? –

+0

내가 대답하는 모든 사람들에게 내 다른 질문은 : 대답하기 전에 질문을 읽고 이해하는 사람은 아무도 없습니까? Jeez .... –

+0

좋아, 다음 질문 : 당신은 무엇을 호출하는거야? 스케줄링 메소드에 알려진 기존의 인스턴스화 된 오브젝트 또는 대상을 인스턴스화하는 중입니까? –

답변

1

나는 당신이 주요 옵션이 생각 :

  1. 을 유형 이름을 매개 변수 값과 함 2 저장하고 Type.GetType(string)을 사용하여 유형 변환하여 해당 유형을 분석하십시오.
  2. 이 방법으로 호출하여 문자열 배열을 수락하고 메서드가 필요한 모든 캐스팅을 수행 할 것으로 기대하는 모든 메서드를 표준화합니다.

나는 옵션 1을 수행하지 않는다고 말했지만, 함수 호출의 관점에서 도움이 될 것입니다.

옵션 2는 모든 값을 문자열로 표현하고 문자열에서 적절한 유형으로 변환/변환 할 수 있다고 가정 할 때 상황을 처리하는 훨씬 더 일반적인 방법입니다. 물론, 호출되는 메소드의 정의를 실제로 제어 할 수있는 경우에만 도움이됩니다.

+0

Thanks Sam. 옵션 2를 마쳤습니다. 내가하고 싶은 것이 아닙니다. 기회가 생길 때 좀 더 생각해 보겠습니다. - 성가신 것은 매개 변수를 문자열로 사용한다는 것입니다. 내가 전달하고자하는 매개 변수의 유형을 알 수 있습니다 (DB를 보거나 반사하여). 나는 변환 할 수 없다. 다시 한번 감사드립니다. – BIDeveloper

2

확인, 질문의 범위는 이동하지만, 일부 다른 솔루션에 내 원래의 관찰과 반대는 여전히 의미합니다.

난 당신이/여기 '제네릭'을 사용할 수 없습니다 생각합니다. 미리 유형을 알지 못하므로 유형을 작성해야하므로 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); 
     } 
    } 

} 
+0

그들은 C# 4.0의 새로운 동적 유형을 사용하거나 다른 언어로 전환 할 수도 있습니다. 왜냐하면 이것이 내가 유지하고자하는 것이 아니기 때문입니다. – ChaosPandion

+0

안녕하세요, 질문에 응답하는 중입니다. ;-). –

-1

.NET 4를 사용하는 경우 다음을 수행 할 수 있습니다.

var result = default(CustomerType); 
if (!Enum.TryParse("Master", out result)) 
{ 
    // handle error 
} 
+0

@Chaos, 그는 유형이 없습니다. 그는 데이터베이스에있는 문자열로부터 매개 변수 값을 생성하고 있습니다. –

+0

@Sky - 질문을 두 번 다시 읽었으며 유형이 없다는 언급이 없습니다. – ChaosPandion

+0

권. 어쩌면 세 번째 시간이 트릭을 할 것입니다! 그는 데이터베이스 행의 문자열에서 메서드베이스와 매개 변수를 작성합니다. 나는 타입을 가지지 않는 것이 유추 될 것이라고 생각할 것입니다 ... –

1

다음은 .NET 3.5에서 사용하는 유용한 확장 방법입니다. 이 확장 방법을 사용할 수와

, 코드는 다음과 같이 수 :

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

을 매개 변수의 기본 값을 공급함으로써, 컴파일러는 열거 당신이 당신의 문자열로 전환 할 할 알게 될 것이다. 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; 
    } 
+0

@Glen, 제가 카오스에게 말했듯이, 당신이 질문에 설명 된 요구 사항을 읽었을 때 그는 타입이 없습니다. 사실 그는 매개 변수가 * enum인지 여부조차 모릅니다.그는 단지 문자열에서 매개 변수 값을 생성 할 수 있어야하고 열거 형을 사용하여 매개 변수 값을 구성하는 방법을 파악할 수 없습니다. –

+0

@ 글렌, 죄송합니다. 비록 그것이 질문을 다루지는 않지만, 이것은 좋은 작은 수업입니다 - 저는 downvote를하고 싶습니다. 실제로 up-vote를합니다. ;-) 내가 그렇게 할 수 있도록 질문을 편집하십시오. –

+0

@Sky ... '당신을 위해 완료되었습니다. 후속 조치를 보내 주셔서 감사합니다. –

0

아직 귀하의 질문을 완전히 이해하지 못하고 있지만 "매개 변수를 제외하고는 모든 것이 정상입니다"라고 말합니다.

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