i need to evaluate what type is a variable to make some switch,there are any way to evaluate a varible to get his type, like val() or something similar. i need to do something for integers and other for string.
i alreaedy try to using a switch, like this,
switch (selector) {
case int :
print('value is a integer');
break;
case String:
print('value is a String');
break;
}
but how i do this, if switch can allow compare mixed type of vars?
thank you
You can use the keyword is or switch over runtimeType :
dynamic foo = 42;
if (foo is int) {
print("Hello");
}
switch (foo.runtimeType) {
case int: {
print("World");
}
}
Consider using is instead of directly using runtimeType. As is works with subclasses. While using runtimeType is a strict comparison.
You can use something like :
if(selector.runtimeType == int) print("Hello")
It's very simple:
dynamic a = "hello";
if (a.runtimeType == int)
print("a is int");
else if (a.runtimeType == String)
print("a is String");
Create this extension:
extension EvaluateType on Object? {
bool get isInt => this is int;
bool get isString => this is String;
}
Usage:
void main() {
Object? foo;
foo = 0;
print(foo.isInt); // true
print(foo.isString); // false
}
Related
I have a Dart enum which looks like this:
enum Gender {
#JsonValue(0)
male,
#JsonValue(1)
female,
}
I have created a dart extension that returns the String name and int value. It looks something like this -
extension GenderExtention on Gender {
String get name {
switch (this) {
default:
return _getDefaultName(this);
}
}
//For the enum male, it returns "Male"
String _getDefaultName(Community value) {
if (value == null) {
return null;
}
String valueStr = value.toString();
String enumName = valueStr.substring(valueStr.indexOf('.') + 1);
return enumName[0].toUpperCase() + enumName.substring(1);
}
int get value {
switch (this) {
case Gender.male:
return 0;
case Gender.female:
return 1;
default:
return null;
}
}
}
This becomes painful for larger enums, especially the value section.
Are there any suggestions on how to get the enum value (0 for #JsonValue(0)) more easily than manually defining it in the extension? Would using something like reflections help here?
the only way to access annotations at run-time is indeed reflection using dart:mirrors. That library is not available when compiling to the web or for Flutter, so it's probably not going to solve your problem.
What you can do here is:
int get value => this.index;
That only works if the values are actually the same as the index (0 for the first declared value, 1 for the next, etc.)
I want to get the parameter types of a Haxe function using a macro and convert them to a shorthand string form, a bit like JNI/Java method signatures, but without a return type.
The motivation here is to provide access to the function parameter types, without having to slowly search through run-time type information at runtime. For example, say you want to construct a graphical widget for calling a function that takes parameters. You will need the type of each function parameter to create the correct spinbox, textbox, and select box widgets needed for tweaking the values that will be passed to the function.
So the question is, how can you save Haxe function parameter types with a macro?
Here is a macro that works for a few basic types, and any abstracts based on those types. It maps the function parameter types to strings. For example, function type String->Float->Int->String->Void maps to sfis, Float->Float->Int to ff etc:
package;
import haxe.macro.Expr;
import haxe.macro.Context;
import haxe.macro.Type;
import haxe.macro.ExprTools;
// Map some Haxe types to string ids
#:enum abstract TypeMapping(String) from (String) {
var BOOL = "b";
var FLOAT = "f";
var INT = "i";
var STRING = "s";
}
class Util
{
public macro static function getParameterTypes(f:Expr):ExprOf<String> {
var type:Type = Context.typeof(f);
if (!Reflect.hasField(type, 'args')) {
throw "Parameter has no field 'args'";
}
var t = type.getParameters()[0];
var args:Array<Dynamic> = Reflect.field(type, 'args')[0];
var signature:String = "";
for (i in 0...args.length) {
switch(args[i].t) {
case TAbstract(t, p):
var underlyingTypeName = Std.string(t.get().type.getParameters()[0]);
switch(underlyingTypeName) {
case "Bool":
signature += TypeMapping.BOOL;
case "Float":
signature += TypeMapping.FLOAT;
case "Int":
signature += TypeMapping.INT;
case "String":
signature += TypeMapping.STRING;
default:
throw "Unhandled abstract function parameter type: " + underlyingTypeName;
}
case CString:
signature += TypeMapping.STRING;
default:
throw "Unhandled function parameter type: " + args[i];
}
}
return macro $v{signature};
}
}
A further problem is how to make this work for all types, rather than just ones you handle explicitly. To do that, you might populate an array of Strings with the type name/class name/path of each function parameter instead, and return that instead of a single String. Here's an attempt at that, note it doesn't work with function parameters (and probably other stuff) yet:
public macro static function getFullParameterTypes(f:Expr):ExprOf<Array<String>> {
var type:Type = Context.typeof(f);
if (!Reflect.hasField(type, 'args')) {
throw "Parameter has no field 'args'";
}
var args:Array<Dynamic> = Reflect.field(type, 'args')[0];
var pos = haxe.macro.Context.currentPos();
var signature:Array<Expr> = [];
for (i in 0...args.length) {
var argType:Type = args[i].t;
var s;
switch(argType) {
case TFun(t, r):
s = EConst(CString("Function"));
throw "Not working with function parameters yet";
case _:
s = EConst(CString(argType.getParameters()[0].toString()));
}
signature.push({expr: s, pos: pos});
}
return macro $a{signature};
}
A more up to date approach..
macro function deflate(fun:haxe.macro.Expr) {
var type = haxe.macro.Context.typeof(fun);
final paramNames = extractFunction(type);
return macro $v{paramNames};
}
// Extract function parameter names
function extractFunction(type):Array<Dynamic> {
return switch type {
case TFun(args, ret): {
var paramNames:Array<Dynamic> = [];
for (p in args) {
final pName = p.name;
paramNames.push(pName);
}
return paramNames;
}
case _: {throw "unable to extract function information";};
}
}
Use it like this
using Macros;
function func(name:String, greeting:String){};
final args = fun.deflate();
trace(args) // output: [name, greeting]
A problem you may face is how to collect the default value of a parameter, consider the example below.
function func(name:String = "Josh", greeting:String = "Hello"){ return '$greeting $name'};
final args = fun.deflate();
trace(args) // output: [name, greeting]
Now let's account for default parameter values by slightly modifying the code:
// Extract function parameter names
function extractFunction(type):Array<Dynamic> {
return switch type {
case TFun(args, ret): {
var paramNames:Array<Dynamic> = [];
for (p in args) {
final pName = p.name;
final v = {name: pName, value: null}; // <= anticipate a value
paramNames.push(v);
}
return paramNames;
}
case _: {throw "unable to extract function information";};
}
}
macro function deflate(fun:haxe.macro.Expr) {
var type = haxe.macro.Context.typeof(fun);
final paramNames:Array<Dynamic> = extractFunction(type);
// extract default param values
switch fun.expr {
case EFunction(f, m):{
for(a in m.args){
for(p in paramNames){
if(p.name == a.name){
if(a.value != null){
switch (a.value.expr){
case EConst(c):{
switch(c){
case CString(v, _):{
p.value = v;
}
case CFloat(f): {
p.value = Std.parseFloat(f);
}
case CInt(i):{
p.value = Std.parseInt(i);
}
case _: throw "unsupported constant value for default parameter";
}
}
case _:
}
}
}
}
}
}
case _:
}
return macro $v{paramNames};
}
So we can now use it like this
function func(name:String = "Josh", greeting:String = "Hello"){ return '$greeting $name'};
final args = Macros.deflate(func);
trace(args) // output: [{name: 'name', value:'Josh', {name:'greeting', value:'Hello'}]
I have two classes ClassOne and ClassTwo. I want to initialize a different one depending on a variable value, i want to do something like:
if(a == "0") {
let b = ClassOne();
}else{
let b = ClassTwo();
}
without having to write it everytime I need it. Something like:
let b = MainClass()
and gets called ClassOne() or ClassTwo() depending on the case, a is a global variable.
In order for this to work the two types should be related either by extending a common base class or by implementing the same protocol. Further, subsequent operations on b would be restricted to these the two classes have in common.
If you are fine with that restriction, you can do it like this:
protocol CommonProtocol {
func foo() -> Double
var bar : Int { get }
}
class ClassOne : CommonProtocol {
...
}
class ClassTwo : CommonProtocol {
...
}
func MainClass() -> CommonProtocol {
if(a == "0") {
return ClassOne()
} else {
return ClassTwo()
}
}
...
let b = MainClass()
b.foo()
print(b.bar)
Note: You could forego all of the above in favor of a completely dynamic approach by following matt's advise.
You can do it, but it isn't going to be useful without further effort. Consider the following:
class ClassOne {}
class ClassTwo {}
Now we proceed to initialize a variable as an instance of one of these classes. To do so, we must type the variable as AnyObject:
var which : Bool { return true /* or false */}
let obj : AnyObject
switch which {
case true:
obj = ClassOne()
case false:
obj = ClassTwo()
}
You now have obj as either a ClassOne instance or a ClassTwo instance. But there's a problem. You don't know which it is. The AnyObject typing preserves the real underlying type (polymorphism), but it also hides the type. Every time you use obj, you will have to test whether it is a ClassOne or a ClassTwo and cast it to that type in order to use it.
if obj is ClassOne {
(obj as! ClassOne).doSomethingClassOneKnowsHowToDo()
}
The question is: is the pain worth the gain? I would suggest that your desire to do this in the first place is probably a Bad Smell and you should revise your intended architecture. Strict static typing is the point of Swift; you are wrong to want to throw it away.
You could use the ternary operator to do it quickly, however you do need to do it every time:
let b = (a == 0) ? ClassOne() : ClassTwo() //If a==0 let b = ClassOne if not let b= ClassTwo.
#dasblinkenlight solution is great but you can also do like this
protocol MyProto {
var x: Int { get }
}
class A: MyProto {
var x = 10
var y = 10
}
class B: MyProto {
var x = 20
}
class Demo {
var type: MyProto!
init(str: String) {
if str == "0" {
type = A()
} else {
type = B()
}
}
}
....
let obj = Demo(str: "0").type
print(obj.x)
I've got an enumeration with a few different cases which are different types, e.g.
enum X {
case AsInt(Int)
case AsDouble(Double)
}
I can switch on these just fine to get the underlying value back out. However, the switch statement is highly annoying with trying to make me execute some code for the other cases that I simply don't care about. For example, right now I have something like
func AsInt(x: X) -> Int? {
switch x {
case AsInt(let num):
return num;
default:
return nil;
}
}
This works but it's pretty tedious always having to reference this method and having to write a new one for each case of each enumeration. What I'm looking for is how to simply attempt to cast a value of type X to one of the cases, like
var object: X = func();
let value = obj as? Int;
if value {
// do shit
}
How can I simply check for a case without having to enumerate all of the cases about which I don't care and execute some non-statement for them?
Bonus points for any solution that can declare value as part of the conditional instead of polluting the scope.
There are actually multiple ways to do it.
Let's do it by extending your enum with a computed property:
enum X {
case asInt(Int)
case asDouble(Double)
var asInt: Int? {
// ... see below
}
}
Solutions with if case
By having let outside:
var asInt: Int? {
if case let .asInt(value) = self {
return value
}
return nil
}
By having let inside:
var asInt: Int? {
if case .asInt(let value) = self {
return value
}
return nil
}
Solutions with guard case
By having let outside:
var asInt: Int? {
guard case let .asInt(value) = self else {
return nil
}
return value
}
By having let inside:
var asInt: Int? {
guard case .asInt(let value) = self else {
return nil
}
return value
}
The last one is my personal favorite syntax of the four solutions.
As of Swift 2 (Xcode 7) this is possible with if/case and
pattern matching:
let x : X = ...
if case let .AsInt(num) = x {
print(num)
}
The scope of num is restricted to the if-statement.
I've got an enumeration with a few different cases which are different types, e.g.
enum X {
case AsInt(Int)
case AsDouble(Double)
}
I can switch on these just fine to get the underlying value back out. However, the switch statement is highly annoying with trying to make me execute some code for the other cases that I simply don't care about. For example, right now I have something like
func AsInt(x: X) -> Int? {
switch x {
case AsInt(let num):
return num;
default:
return nil;
}
}
This works but it's pretty tedious always having to reference this method and having to write a new one for each case of each enumeration. What I'm looking for is how to simply attempt to cast a value of type X to one of the cases, like
var object: X = func();
let value = obj as? Int;
if value {
// do shit
}
How can I simply check for a case without having to enumerate all of the cases about which I don't care and execute some non-statement for them?
Bonus points for any solution that can declare value as part of the conditional instead of polluting the scope.
There are actually multiple ways to do it.
Let's do it by extending your enum with a computed property:
enum X {
case asInt(Int)
case asDouble(Double)
var asInt: Int? {
// ... see below
}
}
Solutions with if case
By having let outside:
var asInt: Int? {
if case let .asInt(value) = self {
return value
}
return nil
}
By having let inside:
var asInt: Int? {
if case .asInt(let value) = self {
return value
}
return nil
}
Solutions with guard case
By having let outside:
var asInt: Int? {
guard case let .asInt(value) = self else {
return nil
}
return value
}
By having let inside:
var asInt: Int? {
guard case .asInt(let value) = self else {
return nil
}
return value
}
The last one is my personal favorite syntax of the four solutions.
As of Swift 2 (Xcode 7) this is possible with if/case and
pattern matching:
let x : X = ...
if case let .AsInt(num) = x {
print(num)
}
The scope of num is restricted to the if-statement.