2010-01-19 15 views
25

W moim kodzie odbicia trafiłem na problem z moją generyczną sekcją kodu. Szczególnie gdy używam ciągu.Jak korzystać z Activator.CreateInstance z ciągami?

var oVal = (object)"Test"; 
var oType = oVal.GetType(); 
var sz = Activator.CreateInstance(oType, oVal); 

Wyjątek

An unhandled exception of type 'System.MissingMethodException' occurred in mscorlib.dll 

Additional information: Constructor on type 'System.String' not found. 

Próbowałem to w celach testowych i występuje w tej jednej wkładki zbyt

var sz = Activator.CreateInstance("".GetType(), "Test"); 

pierwotnie napisałem

var sz = Activator.CreateInstance("".GetType()); 

ale ja się tego błąd

Additional information: No parameterless constructor defined for this object. 

Jak utworzyć ciąg za pomocą odbicia?

Odpowiedz

36

Należy pamiętać, że klasa String jest niezmienna. Nie można go zmienić po utworzeniu. To wyjaśnia, dlaczego nie ma konstruktora bez parametrów, nigdy nie mógłby wygenerować użytecznego obiektu typu string innego niż pusty ciąg znaków. To jest już dostępne w języku C#, jest "".

To samo rozumowanie dotyczy konstruktora ciągu (typu String). Nie ma sensu powielanie łańcucha znaków, łańcuch, który można przekazać do konstruktora, jest już doskonale dobrym przykładem ciągu.

więc rozwiązać problem poprzez badanie dla przypadku strun:

var oType = oVal.GetType(); 
if (oType == typeof(string)) return oVal as string; 
else return Activator.CreateInstance(oType, oVal); 
+2

Rozumiem teraz. Miałem nadzieję, że nie zrobię tego oświadczenia ani żadnych specjalnych czeków. Wygląda na to, że muszę. –

+2

Dzięki. Czy istnieją inne klasy, które mogą wykazać ten problem? lub, czy mogę rozwiązać problem ogólnie (dla dowolnego typu?) –

5

Próbujesz to zrobić:

var sz = new string(); 

spróbować go skompilować, zrozumiesz swój błąd.

Możesz spróbować:

var sz = Activator.CreateInstance(typeof(string), new object[] {"value".ToCharArray()}); 

Ale wygląda na bezużyteczny, należy korzystać bezpośrednio wartość ...

+0

Właściwie on próbuje 'var s = new String ("test")' – leppie

+1

Rozmawiałem o tej linii: ("" GetType()) var sz = Activator.CreateInstance; – Guillaume

2

Wygląda na to, że próbujesz wywołać konstruktora, który po prostu pobiera ciąg - i nie ma takiego konstruktora. Jeśli masz już ciąg znaków, dlaczego próbujesz utworzyć nowy? (Gdy nie podano żadnych dalszych argumentów, próbowano wywołać konstruktor bez parametrów, który ponownie nie istnieje.)

Należy pamiętać, że typeof(string) jest prostszym sposobem uzyskania odniesienia do typu ciągu.

może Pan podać nam więcej informacji na temat większego obrazu tego, co próbujesz zrobić?

+0

Czytam obiekty poprzez odbicie i sqlitedatareader. Nie wiem, czy owal jest ciągiem, chyba że piszę instrukcję if, której chciałem uniknąć. Również używam typ pola i może być tworzenie obiektu, który przyjmuje ciąg znaków. nobugz wymienia ciągi znaków, które są niezmienne, więc myślę, że muszę napisać instrukcję if, aby sprawdzić, czy typ pola to ciąg znaków, czy nie. –

+4

W porównaniu z innymi hackery zaangażowanymi w rekonstruowanie obiektów z łańcuchów, myślę, że prosty "jest to już ciąg" sprawdzenie prawdopodobnie będzie najmniejszym z twoich zmartwień :) –

2

Łańcuch faktycznie nie ma konstruktora, który pobiera ciąg jako dane wejściowe. Jest konstruktorem że trwa tablicy char tak to powinno działać:

var sz = Activator.CreateInstance ("".GetType(), "Test".ToCharArray()); 
2

To właśnie używam w moich projektach. O ile potrzeba stworzenia instancji typu obiektu i niewiedza w czasie projektowania, jest dla mnie raczej normalna. Być może przechodzisz przez właściwości obiektu i chcesz dynamicznie tworzyć ich wszystkie.Wiele razy musiałem utworzyć, a następnie przypisać wartości do niestanowiących instancji obiektów POCO ... z poniższym kodem można użyć wartości ciągu przechowywanej w DB, aby utworzyć instancję obiektu lub utworzyć obiekt przechowywany w bibliotece, która odwołuje się do twojego biblioteka - możesz także ominąć kołowe błędy referencyjne ... Mam nadzieję, że to pomaga.

using System; 
using System.Collections.Generic; 
using System.Globalization; 
using System.Reflection; 

/// <summary> 
/// Instantiates an object. Must pass PropertyType.AssemblyQualifiedName for factory to operate 
/// returns instantiated object 
/// </summary> 
/// <param name="typeName"></param> 
/// <returns></returns> 
public static object Create(string typeAssemblyQualifiedName) 
{ 
    // resolve the type 
    Type targetType = ResolveType(typeAssemblyQualifiedName); 
    if (targetType == null) 
    throw new ArgumentException("Unable to resolve object type: " + typeAssemblyQualifiedName); 

    return Create(targetType); 
} 

/// <summary> 
/// create by type of T 
/// </summary> 
/// <typeparam name="T"></typeparam> 
/// <returns></returns> 
public static T Create<T>() 
{ 
    Type targetType = typeof(T); 
    return (T)Create(targetType); 
} 

/// <summary> 
/// general object creation 
/// </summary> 
/// <param name="targetType"></param> 
/// <returns></returns> 
public static object Create(Type targetType) 
{ 
    //string test first - it has no parameterless constructor 
    if (Type.GetTypeCode(targetType) == TypeCode.String) 
    return string.Empty; 

    // get the default constructor and instantiate 
    Type[] types = new Type[0]; 
    ConstructorInfo info = targetType.GetConstructor(types); 
    object targetObject = null; 

    if (info == null) //must not have found the constructor 
    if (targetType.BaseType.UnderlyingSystemType.FullName.Contains("Enum")) 
     targetObject = Activator.CreateInstance(targetType); 
    else 
     throw new ArgumentException("Unable to instantiate type: " + targetType.AssemblyQualifiedName + " - Constructor not found"); 
    else 
    targetObject = info.Invoke(null); 

    if (targetObject == null) 
    throw new ArgumentException("Unable to instantiate type: " + targetType.AssemblyQualifiedName + " - Unknown Error"); 
    return targetObject; 
} 

/// <summary> 
/// Loads the assembly of an object. Must pass PropertyType.AssemblyQualifiedName for factory to operate 
/// Returns the object type. 
/// </summary> 
/// <param name="typeString"></param> 
/// <returns></returns> 
public static Type ResolveType(string typeAssemblyQualifiedName) 
{ 
    int commaIndex = typeAssemblyQualifiedName.IndexOf(","); 
    string className = typeAssemblyQualifiedName.Substring(0, commaIndex).Trim(); 
    string assemblyName = typeAssemblyQualifiedName.Substring(commaIndex + 1).Trim(); 

    if (className.Contains("[]")) 
    className.Remove(className.IndexOf("[]"), 2); 

    // Get the assembly containing the handler 
    Assembly assembly = null; 
    try 
    { 
    assembly = Assembly.Load(assemblyName); 
    } 
    catch 
    { 
    try 
    { 
     assembly = Assembly.LoadWithPartialName(assemblyName);//yes yes this is obsolete but it is only a backup call 
    } 
    catch 
    { 
     throw new ArgumentException("Can't load assembly " + assemblyName); 
    } 
    } 

    // Get the handler 
    return assembly.GetType(className, false, false); 
}