Problem:
Is it possible to cast dynamically to a type?
For example, could this be possible, using mirrors:
var reflectee = im.getField(simpleName).reflectee;
var converted = testVal as reflectee.runtimeType;
Context:
I want to make a Mixin class which defines a validate method:
abstract class Validatable {
bool validate(Map document) {
}
}
It will iterate over the variables defined for the class where it is mixed in, and checks if the variable in the document are of the same type.
Now, it is working with getting the runtimeType of the respective variables, but it is very restrictive as it does not cast. For example:
var a = 1.1;
var b = 1;
print(a.runtimeType == b.runtimeType); // false
It would be better to better to check with as, but I cant see how to get this to work. Becase:
a = b;
print(a.runtimeType); // int
and not double, as one might expect.
Is it possible?
You could use
import 'dart:mirrors';
class A {
}
class B extends A {
}
class C extends A {
}
void main(args) {
var a = 1.1;
var b = 1;
var x = reflect(b);
print(x.type.isSubtypeOf(reflectType(num)));
print(x.type.isAssignableTo(reflectType(num)));
print(x.type.isAssignableTo(reflectType(double)));
var myb = new B();
print(reflect(myb).type.isSubtypeOf(reflectType(A)));
print(reflect(myb).type.isAssignableTo(reflectType(A)));
print(reflect(myb).type.isAssignableTo(reflectType(C)));
}
Related
I was working on iOS, and now I have to deal with flutter.
The case is when I was using swift, I'm able to access the rule variable with different class type based on different enum cases.
example code as below:
enum SensorTypeRule{
case Lamp(rule:LampRule)
case Counter(rule:CounterRule)
}
struct LampRule{
let ruleTriggerColor: LampColor
let ruleSustainedMilliseconds: UInt32
}
struct CounterRule{
let ruleLimit: UInt32
}
and can be access like below:
let sensorTypeRule:SensorTypeRule = someSensorTypeRuleInstance
switch sensorTypeRule{
case .Lamp(let rule):
print("\(rule. ruleSustainedMilliseconds)")
case .Counter(let rule):
print("\(rule.ruleLimit)")
}
Is there an equivalent approach in dart?
Dart does not have the concept of sealed classes, however you can do this way:
// Create an abstract class representing an enum
// This enum can be instantiated in two ways: either
// calling SensorTypeRule.lamp or SensorTypeRule.counter
abstract class SensorTypeRule {
const factory SensorTypeRule.lamp(LampRule rule) = Lamp._;
const factory SensorTypeRule.counter(CounterRule rule) = Counter._;
const SensorTypeRule();
}
// Create the rules inside each respective class
// Using _ as constructor name disallows the user
// to instantiate it directly -> Lamp(...)
// Instead, it must use the base class -> SensorTypeRule.lamp(...)
class Lamp extends SensorTypeRule {
final LampRule rule;
const Lamp._(this.rule);
}
class Counter extends SensorTypeRule {
final CounterRule rule;
const Counter._(this.rule);
}
// Define the rules
class LampRule {
final LampColor ruleTriggerColor;
final int ruleSustainedMilliseconds;
const LampRule({
required this.ruleTriggerColor,
required this.ruleSustainedMilliseconds,
});
}
class CounterRule {
final int ruleLimit;
const CounterRule({
required this.ruleLimit,
});
}
When accessing it, you can do this way:
final SensorTypeRule sensorTypeRule =
SensorTypeRule.counter(CounterRule(ruleLimit: 10));
if (sensorTypeRule is Lamp) {
print(sensorTypeRule.rule.ruleSustainedMilliseconds);
} else if (sensorTypeRule is Counter) {
print(sensorTypeRule.rule.ruleLimit);
}
This question already has an answer here:
Smart cast (automatic type promotion) using 'is' is not working
(1 answer)
Closed 1 year ago.
I have two related classes: Animal and Cat (which extends Animal) then I make an instance of Cat and check if the instance is type Cat so I set amount of paws to four.
abstract class Animal {
}
class Cat extends Animal {
int? pawsAmount;
}
void main(List<String> arguments) async {
Animal barsik = Cat();
if (barsik is Cat) {
barsik.pawsAmount = 4;
}
}
and it works well BUT the follow code is not:
abstract class Animal {
}
class Cat extends Animal {
int? pawsAmount;
}
class Consumer {
Animal _animal;
Consumer(Animal animal) : _animal = animal;
void init() {
if (_animal is Cat) {
_animal.pawsAmount = 4;
}
}
}
void main(List<String> arguments) async {
Animal barsik = Cat();
if (barsik is Cat) {
barsik.pawsAmount = 4;
}
final consumer = Consumer(barsik);
consumer.init();
}
it has got an error:
bin/constest.dart:17:15: Error: The setter 'pawsAmount' isn't defined for the class 'Animal'.
- 'Animal' is from 'bin/constest.dart'.
Try correcting the name to the name of an existing setter, or defining a setter or field named 'pawsAmount'.
_animal.pawsAmount = 4;
^^^^^^^^^^
if I change
if (_animal is Cat) {
_animal.pawsAmount = 4;
}
to
if (_animal is Cat) {
(_animal as Cat).pawsAmount = 4;
}
it works fine
Is there any way to make the code work?
Thank you!
Dart only promotes local variables.
The _animal is an instance variable, which means that it can potentially change value at any time (the compiler won't start trying to predict whether it actually does), and therefore you can't promote it - the value might change between the check and the later use, making the check an insufficient guarantee.
I would move the value into a local variable before checking it:
void init() {
var animal = _animal;
if (animal is Cat) {
animal.pawsAmount = 4;
}
}
Consider this class that is used as a data model in a Model-View-Controller scenario (I'm using TypeScript 3.5):
export class ViewSource {
private viewName : string;
private viewStruct : IViewStruct;
private rows : any[];
private rowIndex : number|null;
constructor(viewName : string) {
// Same as this.setViewName(viewName);
this.viewName = viewName;
this.viewStruct = api.meta.get_view_struct(viewName);
if (!this.viewStruct) {
throw new Error("Clould not load structure for view, name=" + (viewName));
}
this.rows = [];
this.rowIndex = null;
}
public setViewName = (viewName: string) => {
this.viewName = viewName;
this.viewStruct = api.meta.get_view_struct(viewName);
if (!this.viewStruct) {
throw new Error("Clould not load structure for view, name=" + (viewName));
}
this.rows = [];
this.rowIndex = null;
}
public getViewStruct = ():IViewStruct => { return this.viewStruct; }
public getCellValue = (rowIndex: number, columnName: string) : any => {
const row = this.rows[rowIndex] as any;
return row[columnName];
}
}
This is not a complete class, I only included a few methods to demonstrate the problem. ViewSource is a mutable object. It can be referenced from multiple parts of the application. (Please note that being a mutable object is a fact. This question is not about choosing a different data model that uses immutable objects.)
Whenever I want to change the state of a ViewSource object, I call its setViewName method. It does work, but it is also very clumsy. Every line of code in the constructor is repeated in the setViewName method.
Of course, it is not possible to use this constructor:
constructor(viewName : string) {
this.setViewName(viewName);
}
because that results in TS2564 error:
Property 'viewStruct' has no initializer and is not definitely assigned in the constructor.ts(2564)
I do not want to ignore TS2564 errors in general. But I also do not want to repeat all attribute initializations. I have some other classes with even more properties (>10), and the corresponding code duplication looks ugly, and it is error prone. (I might forget that some things have to bee modified in two methods...)
So how can I avoid duplicating many lines of code?
I think the best method to avoid code duplication in this case would be to create a function that contains the initialization code, but instead of setting the value, it retunrs the value that need to be set.
Something like the following:
export class ViewSource {
private viewName : string;
private viewStruct : IViewStruct;
private rows : any[];
private rowIndex : number|null;
constructor(viewName : string) {
const {newViewName, newViewStruct, newRows, newRowIndex} = this.getNewValues(viewName);
this.viewName = newViewName;
this.newViewStruct = newViewStruct;
// Rest of initialization goes here
}
public setViewName = (viewName: string) => {
const {newViewName, newViewStruct, newRows, newRowIndex} = this.getNewValues(viewName);
// Rest of initialization goes here
}
privat getNewValues = (viewName) => {
const newViewName = viewName;
const newViewStruct = api.meta.get_view_struct(viewName);
if (!newViewStruct) {
throw new Error("Clould not load structure for view, name=" + (viewName));
}
const newRows = [];
const newRowIndex = null;
return {newViewName, newViewStruct, newRows, newRowIndex};
}
}
This way the only thing you duplicate is setting the values, not calculating them, and if the values calculations will get more complicated you can simply expand the returned value.
A less complex approach than the accepted answer is to use the //#ts-ignore[1] comment above each member that is initialized elsewhere.
Consider this contrived example
class Foo {
// #ts-ignore TS2564 - initialized in the init method
a: number;
// #ts-ignore TS2564 - initialized in the init method
b: string;
// #ts-ignore TS2564 - initialized in the init method
c: number;
constructor(a: number, b: string) {
if(a === 0) {
this.init(a,b,100);
} else {
this.init(a,b,4912);
}
}
private init(a: number, b: string, c: number): void {
this.a = a;
this.b = b;
this.c = c;
}
}
Since TypeScript 3.9 there exists the //#ts-expect-error[2] comment, but I think #ts-ignore is suitable.
[1] Suppress errors in .ts files
[2] TS expect errors comment
Since TypeScript 2.7 you can use the definite assignment assertion modifier which means adding an exclamation mark between the variable name and the colon:
private viewName!: string
This has the same effect as adding a // #ts-ignore TS2564 comment above it as #RamblinRose suggested.
Using Dart here.
As the above title suggests, I have a class (shown below) that has three bool instance variables. What I want to do is create a function that inspects the identifier names of these instance variables and prints each of them out in a string. The .declarations getter that comes with the ClassMirror class ALMOST does this, except it also gives me the name of the Constructor and any other methods I have in the class. This is no good. So really what I want is a way to filter by type (i.e., only give me the boolean identifiers as strings.) Any way to do this?
class BooleanHolder {
bool isMarried = false;
bool isBoard2 = false;
bool isBoard3 = false;
List<bool> boolCollection;
BooleanHolder() {
}
void boolsToStrings() {
ClassMirror cm = reflectClass(BooleanHolder);
Map<Symbol, DeclarationMirror> map = cm.declarations;
for (DeclarationMirror dm in map.values) {
print(MirrorSystem.getName(dm.simpleName));
}
}
}
OUTPUT IS:
isMarried
isBoard2
isBoard3
boolsToStrings
BooleanHolder
Sample code.
import "dart:mirrors";
void main() {
var type = reflectType(Foo);
var found = filter(type, [reflectType(bool), reflectType(int)]);
for(var element in found) {
var name = MirrorSystem.getName(element.simpleName);
print(name);
}
}
List<VariableMirror> filter(TypeMirror owner, List<TypeMirror> types) {
var result = new List<VariableMirror>();
if (owner is ClassMirror) {
for (var declaration in owner.declarations.values) {
if (declaration is VariableMirror) {
var declaredType = declaration.type;
for (var type in types) {
if (declaredType.isSubtypeOf(type)) {
result.add(declaration);
}
}
}
}
}
return result;
}
class Foo {
bool bool1 = true;
bool bool2;
int int1;
int int2;
String string1;
String string2;
}
Output:
bool1
bool2
int1
int2
Like the question at Dynamic class method invocation in PHP I want to do this in Dart.
var = "name";
page.${var} = value;
page.save();
Is that possible?
There are several things you can achieve with Mirrors.
Here's an example how to set values of classes and how to call methods dynamically:
import 'dart:mirrors';
class Page {
var name;
method() {
print('called!');
}
}
void main() {
var page = new Page();
var im = reflect(page);
// Set values.
im.setField("name", "some value").then((temp) => print(page.name));
// Call methods.
im.invoke("method", []);
}
In case you wonder, im is an InstanceMirror, which basically reflects the page instance.
There is also another question: Is there a way to dynamically call a method or set an instance variable in a class in Dart?
You can use Dart Mirror API to do such thing. Mirror API is not fully implemented now but here's how it could work :
import 'dart:mirrors';
class Page {
String name;
}
main() {
final page = new Page();
var value = "value";
InstanceMirror im = reflect(page);
im.setField("name", value).then((_){
print(page.name); // display "value"
});
}
You can use Serializable
For example:
import 'package:serializable/serializable.dart';
#serializable
class Page extends _$PageSerializable {
String name;
}
main() {
final page = new Page();
var attribute = "name";
var value = "value";
page["name"] = value;
page[attribute] = value;
print("page.name: ${page['name']}");
}