2013-03-22 27 views
22

In fase di esecuzione ottengo l'istanza in box di qualche tipo. Come cancellarlo in un tipo sottostante?Come annullare l'estrazione dall'oggetto per scriverlo, non conoscendo quel tipo al momento della compilazione?

Object obj; 
String variable = "Some text"; 

obj = variable // boxing; 

// explicit unboxing, because we know the type of variable at compile time. 

var x = (String)obj  

// Now let's pretend that we don't know the type of underlying object at compile time. 

Type desiredType = obj.GetType(); // But we can figure out. 

//And now the question. 
//How to express something like this: 

var y = (desiredType)obj; //Need to get unboxed instance of initial variable here; 
+9

'STRING' è un tipo di riferimento, quindi nessun boxe verificano durante la conversione in' object' –

+0

oggetti di tipo _value hanno due rappresentazioni: un modulo unboxed e una forma scatolata. I tipi di riferimento (come 'stringa') sono sempre in una forma scatolata ._ –

+3

Forse un cattivo esempio con la stringa. Ma mai meno come farlo? –

risposta

7

Ora lascia supporre, che la vera boxe si verificano:

int v = 5; 

object o = v; //boxed 

Type type = o.GetType(); //will return typeof(int) 

int convertedBack = (int)Convert.ChangeType(o, type); 

Console.WriteLine (convertedBack); //prints 5 

Nota, se si sostituisce:

object convertedBack = Convert.ChangeType(o, type); 

Console.WriteLine (convertedBack); //it still prints 5 
Console.WriteLine (o); //it even print 5 here 

La ragione è che oggetto di fondo è ancora int. Ho appena usato questo esempio per dimostrarti che la boxe è irrilevante qui. È necessario fare affidamento su alcune astrazioni nelle operazioni e se si desidera eseguire il cast a int in modo dinamico, quale tipo di riferimento si desidera utilizzare.

+0

OK, ma hai usato casting per int qui. Ciò di cui ho bisogno è smth tipo 'var convertito = (tipo) Convert.ChangeType (o, type);' –

+3

@PaulKyrej a problemi di sintassi a parte: in che modo * possibile * ti aiuterebbe? Cosa potresti fare ora con 'convertbackback 'che non potresti fare quando era' object'? –

+1

@MarcGravell Avanti Q sarà _ "come trovare tutte le cose che posso fare con una cosa di cui non conosco il tipo?" _... –

20

Se non si conosce il tipo al momento della compilazione, allora non si può Unbox perché devi nessun posto dove metterlo - tutto quello che puoi fare è conservarla in un object, che è: in scatola.

Lo stesso vale anche per fare riferimento a tipi come string: non puoi lanciare al tipo giusto se non si conosce il tipo in fase di compilazione: avete nessun posto dove metterlo.

È possibile special-case alcuni tipi, per esempio:

if(obj is int) { 
    int i = (int)obj; 
    ... 
} ... 

Un altro trucco che è volte (non spesso) utile è quello di passare in farmaci generici; quindi invece di parlare in termini di object si sta parlando in termini di T. Questo ha ... un uso limitato però. Il modo più semplice per farlo è tramite dinamica, ad esempio:

dynamic obj = ... 
Foo(obj); 
... 
Foo<T>(T val) { ... code with T ... } 

è anche possibile aggiungere casi particolari a quella appreach:

Foo(string val) { ... code with string ...} 
Foo(int val) { ... code with int ...} 

Tuttavia, francamente mi suggeriscono che potrebbe essere meglio guardare difficile a quello che stai cercando di fare.

+0

+1. Uno avrà bisogno di costruire un tipo generico in fase di esecuzione ovviamente. –

+0

@AlexeiLevenkov perché? francamente non vedo molta utilità in ciò che l'OP sta cercando di fare qui ... –

+0

+1. Grazie Marc, vedere tutte queste risposte di 'Convert.ChangeType' mi ha fatto chiedere:" Come può ChangeType restituire un valore non inserito in un 'oggetto'?" – ybo

-1

si può provare a utilizzare il runtime dinamico

[Test] 
    public void Test_UnboxingAtRuntime() 
    { 
     object boxed = "Hello"; 

     //this line is commented out as it does not compile 
     // OverloadedMethod(boxed); 

     var result = CallCorrectMethod(boxed); 
     Assert.That(result, Is.EqualTo("string")); 

     boxed = 1; 
     result = CallCorrectMethod(boxed); 
     Assert.That(result, Is.EqualTo("int")); 
    } 

    public string CallCorrectMethod(dynamic t) 
    { 
     return OverloadedMethod(t); 
    } 

    public string OverloadedMethod(string s) 
    { 
     return "string"; 
    } 

    public string OverloadedMethod(int s) 
    { 
     return "int"; 
    } 
+1

Non c'è boxe nel tuo esempio per iniziare ... E anche nessuna traccia di unboxing - non sei sicuro di cosa tu suggerisca esattamente qui ... –

+0

Cosa stai parlando? Il cast di 'stringa' in' oggetto' è la definizione del libro di testo di boxe ([vedi questo] (http://msdn.microsoft.com/en-us/library/yz2be5wk.aspx)), e una volta nel metodo generico è stato cancellato come T è uguale a System.String, se questo avesse appena chiamato 'GetTypeName (str)' avrebbe prodotto "System.Object" –

+0

Spiacente, il mio commento precedente ora è un po 'sbagliato quando ho cambiato il campione per renderlo è più chiaro quindi non esiste più un metodo generico. –

3

In questi casi ho intenzione di utilizzare il modello di strategia utilizzando un Dictionary<Type, Action<object>>:

internal class Program 
{ 
    private static void Main(string[] args) 
    { 
     var something = new Something(); 

     something.ComputeValue(13); 
     something.ComputeValue(DateTime.Now); 
     something.ComputeValue(DayOfWeek.Monday); 

     Console.ReadKey(); 
    } 
} 

internal class Something 
{ 
    private static Dictionary<Type, Action<object>> _Strategies; 

    static Something() 
    { 
     // Prepare all available strategies. 
     _Strategies = new Dictionary<Type, Action<object>>(); 
     _Strategies.Add(typeof(int), ComputeInteger); 
     _Strategies.Add(typeof(DateTime), ComputeDateTime); 
    } 

    public void ComputeValue(object value) 
    { 
     Action<object> action; 

     // Check if we have a matching strategy. 
     if (!_Strategies.TryGetValue(value.GetType(), out action)) 
     { 
      // If not, log error, throw exception, whatever. 
      action = LogUnknownType; 
     } 

     // Perform the matching strategy on the given value. 
     action(value); 
    } 

    private static void ComputeDateTime(object source) 
    { 
     // We get an object, but we are sure that it will always be an DateTime. 
     var value = (DateTime)source; 
     Console.WriteLine("We've got an date time: " + value); 
    } 

    private static void ComputeInteger(object source) 
    { 
     // We get an object, but we are sure that it will always be an int. 
     var value = (int)source; 
     Console.WriteLine("We've got an integer: " + value); 
    } 

    private static void LogUnknownType(object source) 
    { 
     // What should we do with the drunken sailor? 
     var unknownType = source.GetType(); 
     Console.WriteLine("Don't know how to handle " + unknownType.FullName); 
    } 
} 
0

Ecco un esempio di esattamente perché si farebbe A fare questo:

class MyClass 
{ 
    public int Id {get;set;} 
    public string Name {get;set;} 
    public decimal Val {get;set;} 
} 
int i = 0; 
var myClassImp = new MyClass(); 
foreach (var val in new [object]{"10", "My name", "100.21"} // Could be read from some data source, such as an excel spreadsheet 
{ 
    var prop = typeof(MyClass).GetProperties().ElementAt(i++); 
    // !!!!!! THROWS EXCEPTION !!!!!!! 
    prop.SetValue(myClassImp, System.Convert.ChangeType(val, prop.PropertyType), null); 
} 

Il motivo per questo è perché il valore è un oggetto in scatola. .. in fase di esecuzione non si conosce il tipo, quindi è necessario annullare l'accesso all'elica.PropertyType

0

Una soluzione pragmatica; cercare di utilizzare un TypeConverter direttamente, e se fallisce, convertire in stringa e viceversa: -

private static T GetValueOfType<T>(this ManagementBaseObject MBO, String FieldName) { 
    T lResult; 

    try { 
     Object lObj = MBO[FieldName]; 

     var lSrcType = lObj.GetType(); 
     var lDestType = typeof(T); 

     if (lDestType.IsValueType && lDestType.IsAssignableFrom(lSrcType)) { 
      lResult = (T)lObj; 
      return lResult; 
     } 

     var lDestTC = TypeDescriptor.GetConverter(typeof(T)); 
     if (lDestTC.CanConvertFrom(lSrcType)) { 
      lResult = (T)lDestTC.ConvertFrom(lObj); 
     } else { 
      var lSrcTC = TypeDescriptor.GetConverter(lSrcType); 
      String lTmp = lSrcTC.ConvertToInvariantString(lObj); 
      lResult = (T)lDestTC.ConvertFromInvariantString(lTmp); 
     } 
    } catch { 
     lResult = default(T); 
    } 
    return lResult; 
} 
0
public static string GetType(object data) 
{ 
    Type type = data.GetType(); 
    return Convert.ChangeType(data, type).GetType().Name; 
} 

Salve, questo metodo riceve oggetto di dati e restituisce stringa del nome del tipo di oggetto. Spero che questo sia ciò di cui hai bisogno.

0

Usa espressioni:

var y = dynamic_cast (obj, desiredType);

static object DynamicCast(object source, Type type) 
{ 
    var parameter = Expression.Parameter(typeof(object), "input"); 

    var cast = Expression.TypeAs(Expression.Convert(parameter, type), typeof(object)); 

    var lambda = Expression.Lambda<Func<object, object>>(cast, parameter); 

    var func = lambda.Compile(); 

    return func(source); 
} 
+0

Non funziona. La riga 'var lambda = Expression.Lambda > (cast, parametro)' errori con 'Espressione di tipo 'System.Nullable1 [System.Int32]' non può essere utilizzata per il tipo restituito 'System.Object' ' –

+0

O per essere più precisi. Non funziona per tipi non annulli come int –

+0

Risolto aggiungendo una conversione aggiuntiva –