2010-10-19 14 views
27

jestem przechowywania wartości obiektów w ciągach npC# rodzajowe ciąg analizowania do dowolnego obiektu

string[] values = new string[] { "213.4", "10", "hello", "MyValue"}; 

jest jakiś sposób, aby rodzajowo zainicjować odpowiednie typy obiektów? na przykład, coś

double foo1 = AwesomeFunction(values[0]); 
int foo2 = AwesomeFunction(values[1]); 
string foo3 = AwesomeFunction(values[2]); 
MyEnum foo4 = AwesomeFunction(values[3]); 

gdzie AwesomeFunction jest funkcja muszę. Ostatecznym zastosowaniem jest intialize właściwości np

MyObject obj = new MyObject(); 
PropertyInfo info = typeof(MyObject).GetProperty("SomeProperty"); 
info.SetValue(obj, AwesomeFunction("20.53"), null); 

Powodem muszę taka funkcjonalność jest ja przechowywania wspomnianych wartości w bazie danych, i chcą je odczytać za pomocą kwerendy, a następnie zainicjować odpowiednie właściwości obiektu . Czy to będzie możliwe? Cały obiekt nie jest przechowywany w bazie danych, tylko kilka pól, które chciałbym odczytać dynamicznie w ustawieniu &. Wiem, że mogę to zrobić statycznie, ale będzie to nudne, trudne do utrzymania i czytane będą podatne na błędy z wieloma różnymi polami/właściwościami.

EDYCJA: Dodatkowe punkty, jeśli AwesomeFunction może pracować z klas niestandardowych, które określają konstruktora, który przyjmuje ciąg!

EDIT2: Typ miejsca docelowego można poznać za pośrednictwem właściwości PropertyType, w konkretnym przypadku, w którym chcę korzystać z tego rodzaju funkcji. Myślę wyliczenia byłoby łatwe do analizowania z tym np

Type destinationType = info.PropertyType; 
Enum.Parse(destinationType, "MyValue"); 
+0

Re „punktów bonusowych” - byłoby lepiej zdefiniować i skojarzyć 'TypeConverter', i że * * obsłużyć to (przekazanie do konstruktora). Ponieważ to właśnie konwertery typu są zaprojektowane dla ... –

Odpowiedz

35

Prawdopodobnie pierwszą rzeczą, aby spróbować to:

object value = Convert.ChangeType(text, info.PropertyType); 

Nie obsługuje to jednak rozszerzalności poprzez niestandardowe typy; jeśli trzeba, że ​​, jak about:

TypeConverter tc = TypeDescriptor.GetConverter(info.PropertyType); 
object value = tc.ConvertFromString(null, CultureInfo.InvariantCulture, text); 
info.SetValue(obj, value, null); 

Lub:

info.SetValue(obj, AwesomeFunction("20.53", info.PropertyType), null); 

z

public object AwesomeFunction(string text, Type type) { 
    TypeConverter tc = TypeDescriptor.GetConverter(type); 
    return tc.ConvertFromString(null, CultureInfo.InvariantCulture, text); 
} 
+0

Wow, szybka odpowiedź! Wygląda na to, że ten kod działa jak smakołyk. Ta klasa TypeConverter jest całkiem słodka, właściwie to, o co mi chodziło. Dzięki! – mike

+7

+ 1.PS: do edycji OP: Jeśli masz inne niestandardowe klasy oprócz int/double/etc, możesz napisać odpowiedni "TypeConverter". Sprawdź [tutaj] (http://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverterattribute.aspx) i [tutaj] (http://msdn.microsoft.com/en-us/library/ ayybcxe5.aspx). –

+0

@mrnye - dla informacji można dość łatwo napisać własne konwertery typów - wystarczy opisać niestandardowe typy za pomocą '[TypeConverter (...)], aby powiedzieć, czego chcesz. Możesz * nawet * zmienić konwertery dla istniejących typów, jeśli chcesz ... –

3

Oto prosty wersja:

object ConvertToAny(string input) 
{ 
    int i; 
    if (int.TryParse(input, out i)) 
     return i; 
    double d; 
    if (double.TryParse(input, out d)) 
     return d; 
    return input; 
} 

rozpozna ints i dwuosobowe, ale wszystko inne jest zwracany jako ciąg znaków. Problem z obsługą wyliczeń polega na tym, że nie ma sposobu, aby dowiedzieć się, do czego należy wartość, a nie można określić, czy powinna ona być ciągiem, czy nie. Inne problemy to, że nie obsługuje daty/czasu lub dziesiętne (jak można odróżnić je od sobowtórów?) Itp

Jeśli jesteś gotów do zmiany kodu tak:

PropertyInfo info = typeof(MyObject).GetProperty("SomeProperty"); 
info.SetValue(obj, AwesomeFunction("20.53", info.PropertyType), null); 

wtedy staje się znacznie łatwiejsze:

object ConvertToAny(string input, Type target) 
{ 
    // handle common types 
    if (target == typeof(int)) 
     return int.Parse(input); 
    if (target == typeof(double)) 
     return double.Parse(input); 
    ... 
    // handle enums 
    if (target.BaseType == typeof(Enum)) 
     return Enum.Parse(target, input); 
    // handle anything with a static Parse(string) function 
    var parse = target.GetMethod("Parse", 
        System.Reflection.BindingFlags.Static | 
        System.Reflection.BindingFlags.Public, 
        null, new[] { typeof(string) }, null); 
    if (parse != null) 
     return parse.Invoke(null, new object[] { input }); 
    // handle types with constructors that take a string 
    var constructor = target.GetConstructor(new[] { typeof(string) }); 
    if (constructor != null) 
     return constructor.Invoke(new object[] { input }); 
} 

Edycja: Dodano brakujący nawias

+0

I tak, taka funkcja (i jak to robisz) zwróci 'obiekt', więc OP powinien być rzutowany na pewien typ:' (int) ConvertToAny ("1") '. Innymi słowy (IMO) - to niemożliwe/zadanie nie ma sensu. Tylko CLR, nie C#, może zawierać funkcje inne niż tylko zwracanie typu: – abatishchev

+0

Aby uzyskać dodatkowe informacje, zobacz EDIT2. W moim konkretnym celu mogę uzyskać typ klasy docelowej za pośrednictwem właściwości PropertyType. To sprawi, że analiza Enums będzie prosta. Umożliwiłoby to również rozróżnienie między zmiennymi/podwójnymi/dziesiętnymi/itd. Miałem jednak nadzieję, że nie piszę dużej instrukcji if/case dla każdego typu klasy (choć domyślam się, że nie byłoby ** że ** niechlujnie). – mike

+0

myrne: Odpowiedź Marca może być jeszcze lepsza, ale zmodyfikowałem moją kopalnię, aby obsługiwać wyliczenia, konstruktory, które pobierają ciąg znaków i wszystko z funkcją Parse. – Gabe

0

wiem, że to nie jest odpowiedź na to pytanie, ale masz spojrzał na Dapper micro ORM ?
Jest über-prosty (w porównaniu do LINQ do SQL lub z tego powodu Entity Framework) i robi to, co chcesz.

Rozważmy to:

public class Dog 
{ 
    public int? Age { get; set; } 
    public Guid Id { get; set; } 
    public string Name { get; set; } 
    public float? Weight { get; set; }  
}    

var guid = Guid.NewGuid(); 
var dog = connection.Query<Dog>("select * from Dogs where Id = @Id", 
    new { Id = 42 } 
).First(); 

sam Wytworny jest pakowany w single file, a według doniesień, is used by StackOverflow team (oprócz Linq SQL).

3
public T Get<T>(string val) 
{ 
    if (!string.IsNullOrWhiteSpace(val)) 
     return (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromString(val); 
    else 
     return default(T); 
} 
+1

Byłoby miło wyjaśnienie, co to jest kod, zamiast tylko wklejać ciąg znaków do kodu – Joel

+0

do dowolnego generycznego – RouR

+2

miałem na myśli uderzenie w przycisk [edit] i dodanie kontekstu! :) – Joel

Powiązane problemy