2013-04-19 11 views
5

Ho implementato una conversione esplicita da stringa ad oggetto chiamata Foo.Conversione esplicita generica

Quindi =>Foo f = (Foo) "dati foo"; funziona

Ho bisogno di implementare una funzione che esegue il cast di una stringa sul generico T, T in questo caso è Foo datatype.

public T Get<T>(object o){ 
     // this always return false 
     if (typeof(T).IsAssignableFrom(typeof(String))) 
     { 
      // when i by pass the if above this throws invalid cast exception 
      return (T)(object)str; 
     } 
     return null; 
} 

// When I call this, it generated an error 
// Invalid cast from 'System.String' to Foo 
Foo myObj = Get<Foo>("another foo object"); 

// when I use the dynamic keyword it works but this is C# 4.0+ feature, my function is in the older framework 
return (T)(dynamic)str; 
+1

Si dovrebbe usare 'TypeConverter's invece. –

+0

Qual è la definizione di 'Foo'? Ha 'implicito' (http://msdn.microsoft.com/en-us/library/z5z9kes2%28v=vs.71%29.aspx) o' esplicito' (http://msdn.microsoft.com/ operatori di en-us/library/xhbhezf4% 28v = vs.71% 29.aspx) sovraccaricati? – CodingWithSpike

+0

@CodingWithSpike Foo ha una conversione esplicita Foo itm = (Foo) "stringa" –

risposta

2

Un esempio che utilizza la riflessione:

class Program 
{ 
    static void Main(string[] args) 
    {   
     Foo myObj = TypeResolver.Get<Foo>("Foo data");    
    } 
} 

class TypeResolver 
{ 
    public static T Get<T>(object obj) 
    { 
     if (typeof(T).CanExplicitlyCastFrom<string>()) 
     {        
      return obj.CastTo<T>(); 
     } 
     return default(T); 
    } 
} 

public static class Extensions 
{ 
    public static bool CanExplicitlyCastFrom<T>(this Type type) 
    { 
     if (type == null) 
      throw new ArgumentNullException("type"); 

     var paramType = typeof(T); 
     var castOperator = type.GetMethod("op_Explicit", 
             new[] { paramType }); 
     if (castOperator == null) 
      return false; 

     var parametres = castOperator.GetParameters(); 
     var paramtype = parametres[0]; 
     if (paramtype.ParameterType == typeof(T)) 
      return true; 
     else 
      return false; 
    } 

    public static T CastTo<T>(this object obj) 
    {    
     var castOperator = typeof(T).GetMethod("op_Explicit", 
             new[] { typeof(string) }); 
     if (castOperator == null) 
      throw new InvalidCastException("Can't cast to " + typeof(T).Name); 
     return (T)castOperator.Invoke(null, new[] { obj }); 
    } 
} 
+0

Questo è esattamente quello di cui ho bisogno, grazie !!! var castOperator = type.GetMethod ("op_Explicit", new [] {paramType}); –

1

Se si va via (object) allora semplicemente non un cast di tipo assegno o una scatola/Unbox (in termini IL: un Unbox-any) - esso non utilizzare gli operatori. L'unico modo per utilizzare generici e operatori insieme è tramite (dynamic) anziché (object), ma questo funziona un po 'in fase di esecuzione.

+0

Ho accennato che nella mia ultima riga, dynmic è la funzione C# 4.0 Sono bloccato nel vecchio framework. –

+1

@DuD. quindi non puoi farlo tramite i generici; questo è l'unico modo. Il punto sui generici è che lo stesso IL si applica a tutti i tipi. Questo non funziona per gli operatori. –

0

Questo è reallllly brutto, ma il test viene superato:

using Microsoft.VisualStudio.TestTools.UnitTesting; 

namespace UnitTestProject1 
{ 
    [TestClass] 
    public class UnitTest2 
    { 
     public T Get<T>(string str) 
      where T : CanCastFromString<T>, ICanInitFromString, new() 
     { 
      return (T)str; 
     } 

     [TestMethod] 
     public void Test() 
     { 
      var result = Get<Foo>("test"); 

      Assert.IsNotNull(result); 
      Assert.IsInstanceOfType(result, typeof(Foo)); 
      Assert.AreEqual("test", result.Value); 
     } 
    } 

    public class Foo : CanCastFromString<Foo> 
    { 
     public string Value { get; set; } 

     public override void InitFromString(string str) 
     { 
      Value = str; 
     } 
    } 

    public abstract class CanCastFromString<T> : ICanInitFromString 
     where T : CanCastFromString<T>, ICanInitFromString, new() 
    { 
     public static explicit operator CanCastFromString<T>(string str) 
     { 
      var x = new T(); 
      x.InitFromString(str); 
      return x; 
     } 

     public abstract void InitFromString(string str); 
    } 

    public interface ICanInitFromString 
    { 
     void InitFromString(string str); 
    } 
} 

si può ingannare il compilatore in sapendo che il generico T può essere esplicitamente espressi dal string definendolo su un abstract classe CanCastFromString quindi vincolante la tua funzione generica Get() per quella classe astratta.

2

Dai un'occhiata anche a this answer da @Jon Skeet - e in particolare la citazione su IsAssignableFrom.

Non penso che sia possibile in un modo che hai immaginato.

Suggerirei di mettere un 'contratto di interfaccia' sulle tue classi di Foo - e quindi lasciare che i generici facciano il loro lavoro.

ad es. qualcosa di simile - ma questo è solo una soluzione veloce ho digitato ...

class Factory 
{ 
    public static T Create<T, TVal>(TVal obj) where T : class, IFoo<TVal>, new() 
    { 
     return new T { Value = obj }; // return default(T); 
    } 
} 
interface IFoo<TVal> 
{ 
    TVal Value { get; set; } 
} 
class Foo : IFoo<string> 
{ 
    public string Value { get; set; } 
    public Foo() { } 
} 
// ... 
public T Get<T, TVal>(TVal obj) where T : class, IFoo<TVal>, new() 
{ 
    return Factory.Create<T, TVal>(obj); 
} 

E si può chiamare in un modo simile - a condizione che si have that luxury - conoscere i tipi ecc
(ma si può lavorare questo fuori e apportare le modifiche necessarie)

Foo foo = Get<Foo, string>("another text"); 
+0

Quindi Type.IsAssignableFrom è fuori questione per questo. A meno che non implementi la conversione utilizzando altri metodi. –

+0

Temo di sì - anche espliciti/impliciti non sembrano dare il via, in modo simile, nel "mondo dei generici" (abbiamo bisogno che Eric spieghi i meccanismi interni qui). Con le interfacce puoi effettivamente far rispettare i tuoi generici per ascoltarti. – NSGaga

0

presumo definita T in classe, ma in entrambi i casi ho sul metodo. Questo funziona se è possibile gestire un vincolo di classe su T.

namespace TestCast { 
    class Program 
    { 
     public static T Get<T>(string o) where T : class 
     { 
      return o as T; 
     } 

     static void Main(string[] args) 
     { 
      Get<Breaker>("blah"); 
     } 
    } 
} 

Restituisce null se la conversione non è valido, proprio come nella domanda di Du ritorna null se non è in grado di convertire. Per una stringa, questo funzionerà in scenari limitati. L'operatore as anche non utilizza operatori di conversione definiti dall'utente.

+0

Il mio metodo non ha alcun vincolo su T. Per rendere il mio post più semplice non ho mostrato altri tipi di conversioni nel metodo. –

+0

Non intendo insidiarvi qui :) ma non penso che funzioni - "restituisce null" in realtà. Compilare però. – NSGaga

+0

Restituisce null se la conversione non è valida, proprio come nella domanda di Du restituisce null se non è possibile convertire. Dalla domanda originale non era chiaro quali fossero le esigenze circostanti. Questo funziona, ma in casi limitati. – AaronLS

Problemi correlati