How can I use Reflection (Mirrors) to access the method names in a Dart Class? - dart

I need to "fetch" the methods in a Dart Class.
How can I do this?
And I want to be able to call the methods.
May I see an example?

Here's an easy copy-pasteable code sample:
import 'dart:mirrors';
import 'dart:io';
main() {
var im = reflect(new File('test')); // Retrieve the InstanceMirror of some class instance.
im.type.methods.values.forEach((MethodMirror method) => print(method.simpleName));
}
Output is:
existsSync
_delete
exists
directory
_getDecodedLines
readAsTextSync
readAsBytesSync
readAsLinesSync
_directory
throwIfError
lastModifiedSync
readAsLines
open
_ensureFileService
deleteSync
delete
_exists
length
openInputStream
create
_create
readAsText
_openStdioSync
openOutputStream
_fullPath
_lastModified
fullPathSync
readAsBytes
lastModified
_openStdio
_open
openSync
lengthSync
directorySync
fullPath
createSync
_lengthFromName

Here is a basic example:
(Note: You will want to have a (very) up to date version of the SDK for this, this was done in Dart Editor version 0.2.1_r14167, Dart SDK version 0.2.1.2_r14167 Nov 2, 2012)
My most sincere Thanks to Gilad of the Google Dart Team for providing this example!
#import('dart:mirrors');
class MyClass {
String _test;
String get test => _test;
set test(String paramVal) => _test = paramVal;
void my_method() {
}
void print_test(){
print("test string is: ${_test}");
}
MyClass(String test) {
_test = test;
}
}
main() {
MyClass myClass = new MyClass("Make my day, PUNK.");
myClass.print_test();
//ClassMirror myClassMirror = reflect(myClass).type;
InstanceMirror myClassInstanceMirror = reflect(myClass);
ClassMirror MyClassMirror = myClassInstanceMirror.type;
Map<String, MethodMirror> map = MyClassMirror.methods;
print("map = ${map}");
map.values.forEach( (MethodMirror mm){
myClassInstanceMirror.invoke(mm.simpleName,[]);
});
}

Concerning Reflection I have just written a couple of "Helper Functions" for fetching a LIST of the method names (not a Map) and invoking the method... all I can say for sure is that it works right now. There are likely technical reasons for not doing it this way - but in my case this does not run in a complex environment. However, I do think they nicely mask over a lot of details that not everybody is going to want to deal with.
Here's a functioning demonstration with and without the Helper functions:
#import('dart:mirrors');
class MyClass {
String _test;
String get test => _test;
set test(String paramVal) => _test = paramVal;
void my_method1(){print("in method1");}
void my_method2(){print("in method2");}
void print_test(){
print("test string is: ${_test}");
}
MyClass(String test) {
_test = test;
}
}
//Helper Methods
InstanceMirror hMr;
List REFLECT_methods(Object myClass) {hMr=reflect(myClass);return(hMr.type.methods.values);}
REFLECT_invoke(MethodMirror mm){hMr.invoke(mm.simpleName, []);}
main() {
MyClass myClass = new MyClass("Make my day, PUNK.");
print("\n=======Using Raw Reflection================");
InstanceMirror myClassInstanceMirror = reflect(myClass);
ClassMirror MyClassMirror = myClassInstanceMirror.type;
Map<String, MethodMirror> map1 = MyClassMirror.methods;
map1.values.forEach( (MethodMirror mm){
myClassInstanceMirror.invoke(mm.simpleName,[]);
});
print("\n==========Using Helper functions=============");
List list2 = REFLECT_methods(myClass);
list2.forEach((method){
REFLECT_invoke(method);
});
}

Related

Dart create class instance by string with class name

I want to invoke functions of a class by their names inside a string. I know my best option are Mirrors.
var ref = reflect(new TestClass());
ref.invoke(Symbol("test"), []);
It works fine, I can call the function test by a string. But I also want to put "TestClass" inside a string. Is it possible somehow ?
var ref = reflect("TestClass");
ref.invoke(Symbol("test"), []);
Jonas
You can do something like this:
import 'dart:mirrors';
class MyClass {
static void myMethod() {
print('Hello World');
}
}
void main() {
callStaticMethodOnClass('MyClass', 'myMethod'); // Hello World
}
void callStaticMethodOnClass(String className, String methodName) {
final classSymbol = Symbol(className);
final methodSymbol = Symbol(methodName);
(currentMirrorSystem().isolate.rootLibrary.declarations[classSymbol]
as ClassMirror)
.invoke(methodSymbol, <dynamic>[]);
}
Note, that this implementation does require that myMethod is static since we are never creating any object but only operate directly on the class itself. You can create new objects from the class by calling newInstance on the ClassMirror but you will then need to call the constructor.
But I hope this is enough. If not, please ask and I can try add some more examples.

`nameof` operator in flutter

There is nameof operator in C#, it allows to get property name at compile time:
var name = nameof(User.email);
Console.WriteLine(name);
//Prints: email
It is not possible to use reflection in flutter and I do not want to hardcode names of properties i.e. to be used for querying SQLite tables. Is there any workaround?
***Currently I'm using built_value library.
For the archives, I guess, this isn't possible as Dart doesn't store the names of variables after compiling, and as you mentioned, Flutter doesn't support reflection.
But you can still hardcode responsibly by grouping your properties as part of the object that they belong to, like with JSON:
class User {
final String email;
final String name;
const User({required this.email, required this.name});
Map toJson() => {
"email": email,
"name": name,
};
}
Instead of remembering to type out "email" and "name" whenever you use User, just call User.toJson(). Then, when you want to rename your variables, you can use your IDE's "rename all", or just skim over your User class to quickly change all of the names without missing any.
I'm currently monitoring the progress on the dart:mirrors package, which offers some neat reflective properties and methods, though, I hadn't found a simple way to just get the name of a symbol like nameof() does.
Example:
import 'dart:mirrors';
class User {
final String email;
final String name;
const User({required this.email, required this.name});
}
void main() {
reflectClass(User).declarations.forEach((key, value) {
print(value.simpleName);
});
}
Output:
Symbol("email")
Symbol("name")
Symbol("User")
These are of type Symbol.
More here: https://api.dart.dev/stable/2.4.0/dart-mirrors/dart-mirrors-library.html
So, until they develop a nameof, I've created an extension on symbol:
extension SymbolExtensions on Symbol {
String get nameof =>
RegExp(r'"(.*?)"').firstMatch(toString())!.group(1).toString();
}
So, you could do:
print(reflectClass(User)
.declarations[#email)]!
.simpleName
.nameof);
Output:
email
It's a start. Dart is still growing.
You can use code generation.
The basic idea is to create a nameof annotation class and mark parts of your code with it. You also need to create a code generator that handles your annotated code. Look at the json_serializable package for an example and create your own code generator.
If you do not want to create your own generator, use a ready-made package nameof: https://pub.dev/packages/nameof
Short how-to with this package.
Mark your class with nameof annotation.
#nameof
class Car {
final double price;
final double weigth;
final int year;
final String model;
Car(this.price, this.weigth, this.year, this.model);
Car.sedan(double price, double weigth, int year)
: this(price, weigth, year, 'Sedan');
}
Run the code generator.
flutter pub run build_runner build
Then use the generated class, which will look something like this.
/// Container for names of elements belonging to the [Car] class
abstract class NameofCar {
static const String className = 'Car';
static const String constructor = '';
static const String constructorSedan = 'sedan';
static const String fieldPrice = 'price';
static const String fieldWeigth = 'weigth';
static const String fieldYear = 'year';
static const String fieldModel = 'model';
}
You can implement your own nameOf function:
String? nameOf(dynamic o) {
if (o == null) return "null";
try {
if (o is List) {
var first = o.length > 0 ? o[0] : null;
if (first != null) {
var elementType = nameOf(first)!;
Log.debug("nameOf: List<$elementType>");
if (!isMinified(elementType))
return "List<$elementType>";
}
} else {
Function? getTypeName = o.getTypeName;
if (getTypeName != null) return getTypeName();
}
} catch (e) {
Log.debug("ignored nameOf error: $e, falling back to o.runtimeType: ${o.runtimeType}");
}
return o.runtimeType.toString();
}
bool isMinified(String type) => type.startsWith("minified:");

How are arguments passed into the parameter list of ClassMirror.newInstance(...)? [duplicate]

I'm perfectly willing to play with this until I get it right, but was hoping someone might give me a hint. The parameter is declared in the docs (gen-dartdocs/dart-mirrors/ClassMirror/newInstance.html) as
InstanceMirror newInstance(Symbol constructorName,
List positionalArguments,
[Map<Symbol,dynamic> namedArguments]);
There is a nice writeup on the format of positionalArguments and namedArguments in the docs. However, it is just a little on the abstract side of my current tolerance level.
A decent discussion also exists at
http://japhr.blogspot.com/2014/06/dart-factory-method-pattern.html
But, alas, no examples of actually passing args into the method.
In my case, I would like to simply pass two args, "title" and "description" into an unnamed subclass constructor.
Here's my code so far:
file: item.dart
import 'dart:mirrors';
abstract class Item {
String title;
String description;
factory Item(String type) {
MirrorSystem libs = currentMirrorSystem();
LibraryMirror lib = libs.findLibrary(new Symbol('app.models'));
Map<Symbol, Mirror> classes = lib.declarations;
// To do: handle exception if class not found
ClassMirror cls = classes[new Symbol(type)];
// TODO:
// verify each subclass has no-arg ctor
// determ how to pass args to ctor.
InstanceMirror inst = cls.newInstance(new Symbol(''), []);
return inst.reflectee;
}
// conflicts w/ Item factory
// Item(this.title, this.description);
}
And here's the class that gets instantiated:
file: model.dart
library app.models;
import 'item.dart' show Item;
/// The barebones model for a codelab. Defines constants used for validation.
class Codelab implements Item {
// ...
}
Finally, here is how the Item factory is called. ItemElement is the superclass of its own hierarchy, subclassed by CodelabElement:
file: item_element.dart:
import 'item.dart' show Item;
class ItemElement {
Item item;
final String itemType;
ItemElement() {
item = new Item(itemType);
}
// ...
}
And CodelabElement:
file: codelab_element.dart
import 'model.dart' show Codelab;
import 'item_element.dart' show ItemElement;
class CodelabElement extends ItemElement {
final itemType = "Codelab";
CodelabElement() : super() {}
//...
}
And then:
file: main.dart
void main() {
var element = new CodelabElement();
}
Currently, the new Codelab instance is returned from newInstance() (very cool), but it doesn't contain the inherited 'title' and 'description' attrs.
Maybe it has something to do with my being unclear on the usage of "extends" and "implements".
This should work
cls.newInstance(new Symbol(''), ['a', 1] /*,
{#arg1Name: 'arg1Value', #arg2Name: 'arg2Value'}*/ );
and is like
new MyClass('a', 1, arg1Name: 'arg1Value' /*, arg2Name: 'arg2Value'*/);
Just saw, Named arguments are not implemented.
You can try it in DartPad

How do I get an actual object dynamically in Dart? [duplicate]

In dart is it possible to instantiate a class from a string?
For example:
vanilla in javascript:
var myObject = window[classNameString];
Objective-C:
id myclass = [[NSClassFromString(#"MyClass") alloc] init];
You need to know the library name and class name to make things work properly. Assume you know both, the below example will instantiate the TestClass and call doStuff on it.
library test;
import "dart:mirrors";
class TestClass {
doStuff() => print("doStuff was called!");
}
main() {
MirrorSystem mirrors = currentMirrorSystem();
LibraryMirror lm = mirrors.libraries['test'];
ClassMirror cm = lm.classes['TestClass'];
Future tcFuture = cm.newInstance('', []);
tcFuture.then((InstanceMirror im) {
var tc = im.reflectee;
tc.doStuff();
});
}
A few notes about this solution:
The library test we are trying to load the class from is already imported in the VM, which makes this case a bit easier.
the call the newInstance allows for passing parameters to the constructor. Positional arguments are implemented, but named parameters are not yet implemented (as of the M2 release).
newInstance returns a Future to allow it to work across isolates.
The syntax has changed.
I got it working this way
library test;
import "dart:mirrors";
class TestClass {
doStuff() => print("doStuff was called!");
}
main() {
MirrorSystem mirrors = currentMirrorSystem();
LibraryMirror lm = mirrors.libraries.values.firstWhere(
(LibraryMirror lm) => lm.qualifiedName == new Symbol('test'));
ClassMirror cm = lm.declarations[new Symbol('TestClass')];
InstanceMirror im = cm.newInstance(new Symbol(''), []);
var tc = im.reflectee;
tc.doStuff();
}
If there are more libraries named 'test' this will fail though.
Try:
Map models = {"Player": Player.instatiate};
var player = models["Player"]();
class Player{
static instatiate() => Player();
}
This was an issue that has plagued me until I figured that I could implement a crude from method to handle the conversion of encoded Json Objects/strings or Dart Maps to the desired class.
Below is a simple example that also handles nulls and accepts JSON (as the string parameter)
import 'dart:convert';
class PaymentDetail
{
String AccountNumber;
double Amount;
int ChargeTypeID;
String CustomerNames;
PaymentDetail({
this.AccountNumber,
this.Amount,
this.ChargeTypeID,
this.CustomerNames
});
PaymentDetail from ({ string : String, object : Map }) {
var map = (object==null) ? (string==null) ? Map() : json.decode(string) : (object==null) ? Map() : object;
return new PaymentDetail(
AccountNumber : map["AccountNumber"] as String,
Amount : map["Amount"] as double,
ChargeTypeID : map["ChargeTypeID"] as int,
CustomerNames : map["CustomerNames"] as String
);
}
}
Below is it's implementation
PaymentDetail payDetail = new PaymentDetail().from(object: new Map());
PaymentDetail otherPayDetail = new PaymentDetail().from(object: {"AccountNumber": "1234", "Amount": 567.2980908});
Once again, this is simplistic and tedious to clone throughout the project but it works for simple cases.

how can i to instance an object by the classname in dart [duplicate]

In dart is it possible to instantiate a class from a string?
For example:
vanilla in javascript:
var myObject = window[classNameString];
Objective-C:
id myclass = [[NSClassFromString(#"MyClass") alloc] init];
You need to know the library name and class name to make things work properly. Assume you know both, the below example will instantiate the TestClass and call doStuff on it.
library test;
import "dart:mirrors";
class TestClass {
doStuff() => print("doStuff was called!");
}
main() {
MirrorSystem mirrors = currentMirrorSystem();
LibraryMirror lm = mirrors.libraries['test'];
ClassMirror cm = lm.classes['TestClass'];
Future tcFuture = cm.newInstance('', []);
tcFuture.then((InstanceMirror im) {
var tc = im.reflectee;
tc.doStuff();
});
}
A few notes about this solution:
The library test we are trying to load the class from is already imported in the VM, which makes this case a bit easier.
the call the newInstance allows for passing parameters to the constructor. Positional arguments are implemented, but named parameters are not yet implemented (as of the M2 release).
newInstance returns a Future to allow it to work across isolates.
The syntax has changed.
I got it working this way
library test;
import "dart:mirrors";
class TestClass {
doStuff() => print("doStuff was called!");
}
main() {
MirrorSystem mirrors = currentMirrorSystem();
LibraryMirror lm = mirrors.libraries.values.firstWhere(
(LibraryMirror lm) => lm.qualifiedName == new Symbol('test'));
ClassMirror cm = lm.declarations[new Symbol('TestClass')];
InstanceMirror im = cm.newInstance(new Symbol(''), []);
var tc = im.reflectee;
tc.doStuff();
}
If there are more libraries named 'test' this will fail though.
Try:
Map models = {"Player": Player.instatiate};
var player = models["Player"]();
class Player{
static instatiate() => Player();
}
This was an issue that has plagued me until I figured that I could implement a crude from method to handle the conversion of encoded Json Objects/strings or Dart Maps to the desired class.
Below is a simple example that also handles nulls and accepts JSON (as the string parameter)
import 'dart:convert';
class PaymentDetail
{
String AccountNumber;
double Amount;
int ChargeTypeID;
String CustomerNames;
PaymentDetail({
this.AccountNumber,
this.Amount,
this.ChargeTypeID,
this.CustomerNames
});
PaymentDetail from ({ string : String, object : Map }) {
var map = (object==null) ? (string==null) ? Map() : json.decode(string) : (object==null) ? Map() : object;
return new PaymentDetail(
AccountNumber : map["AccountNumber"] as String,
Amount : map["Amount"] as double,
ChargeTypeID : map["ChargeTypeID"] as int,
CustomerNames : map["CustomerNames"] as String
);
}
}
Below is it's implementation
PaymentDetail payDetail = new PaymentDetail().from(object: new Map());
PaymentDetail otherPayDetail = new PaymentDetail().from(object: {"AccountNumber": "1234", "Amount": 567.2980908});
Once again, this is simplistic and tedious to clone throughout the project but it works for simple cases.

Resources