2011-10-20 17 views
7

Próbuję wdrożyć klasę podobną do C#'s ExpandoObject w Scala. Jak to ma działać:Implementacja ExpandoObject w Scali

val e = new ExpandoObject 
e.name := "Rahul" // This inserts a new field `name` in the object. 
println(e.name) // Accessing its value. 

Oto, co starałem dotąd:

implicit def enrichAny[A](underlying: A) = new EnrichedAny(underlying) 
class EnrichedAny[A](underlying: A) { 
    // ... 
    def dyn = new Dyn(underlying.asInstanceOf[AnyRef]) 
} 

class Dyn(x: AnyRef) extends Dynamic { 
    def applyDynamic(message: String)(args: Any*) = { 
    new Dyn(x.getClass.getMethod(message, 
     args.map(_.asInstanceOf[AnyRef].getClass) : _*). 
     invoke(x, args.map(_.asInstanceOf[AnyRef]) : _*)) 
    } 
    def typed[A] = x.asInstanceOf[A] 
    override def toString = "Dynamic(" + x + ")" 
} 

class ExpandoObject extends Dynamic { 
    private val fields = mutable.Map.empty[String, Dyn] 
    def applyDynamic(message: String)(args: Any*): Dynamic = { 
    fields get message match { 
     case Some(v) => v 
     case None => new Assigner(fields, message).dyn 
    } 
    } 
} 

class Assigner(fields: mutable.Map[String, Dyn], message: String) { 
    def :=(value: Any): Unit = { 
    fields += (message -> value.dyn) 
    } 
} 

Kiedy próbuję skompilować ten kod, otrzymuję StackOverflowError. Proszę, pomóż mi zdobyć tę pracę. :) Dzięki.

Odpowiedz

4

Gotowy do pracy po zabawie. Rozwiązanie nie jest jednak bezpieczne (ale w tym przypadku nie ma znaczenia, ponieważ celem tego małego narzędzia jest obejście systemu typów :-)

trait ExpandoObject extends Dynamic with mutable.Map[String, Any] { 
    lazy val fields = mutable.Map.empty[String, Any] 
    def -=(k: String): this.type = { fields -= k; this } 
    def +=(f: (String, Any)): this.type = { fields += f; this } 
    def iterator = fields.iterator 
    def get(k: String): Option[Any] = fields get k 
    def applyDynamic(message: String)(args: Any*): Any = { 
    this.getOrElse(message, new Assigner(this, message)) 
    } 
} 

implicit def anyToassigner(a: Any): Assigner = a match { 
    case x: Assigner => x 
    case _ => sys.error("Not an assigner.") 
} 

class Assigner(ob: ExpandoObject, message: String) { 
    def :=(value: Any): Unit = ob += (message -> value) 
} 
+0

Czy możesz podać przykład użycia? –

+0

W moim konkretnym przypadku muszę przetworzyć niektóre dane strukturalne w arkuszach Excela, o których nic nie wiem w czasie kompilacji. Traktowanie tych danych jako zapisów na poziomie języka upraszcza przetwarzanie. – missingfaktor

+0

Możesz odnieść się do powyższego artykułu, aby zobaczyć więcej przykładów. – missingfaktor