Use question mark operator when calling method on possible null value? - dart

I have a varible containing an object which might be null. When trying to call a method I have to check for null. I want the result to be false if the variable is null. What is considered good and readable style to to this?
e.g.
class MyClass {
bool boolMethod() {
return true;
}
}
void main() {
MyClass mc = new MyClass();
MyClass mcnull = null;
bool val1 = mc?.boolMethod();
bool val1null = mcnull?.boolMethod();
bool val2 = mc != null && mc.boolMethod();
bool val2null = mcnull != null && mcnull.boolMethod();
}
Especially when used in if-statements I consider the first version much more readable:
if (mc?.boolMethod())...
versus
if (mc != null && mc.boolMethod())...
But IntelliJ gives me the hint The value of the '?.' operator can be 'null' which isn't appropriate as an operand of a locaical operator. (null_aware_in_logical_operator). Ok - this is right because when the variable is null then I use the null as a boolean value. But it's ok in my case and I try to avoid suppressing warnings.
What is the suggested way? Other ideas?

I think a common pattern is
bool val1 = (mc?.boolMethod() ?? false);
The parens aren't required here but if such expressions are embedded into more complex expressions they are often necessary to get the expected behavior because of the low priority of ??

Related

What is the difference between ?? and =?? in Dart?

In the code below to implement the Singleton design pattern, we approach two different ways.
One of them creates Singleton but the other creates an object each time we call it.
What's the difference between ?? and =?? in the code below written in dart?
class ExampleStateByDefinition extends ExampleStateBase {
static ExampleStateByDefinition? _instance;
ExampleStateByDefinition._internal() {
initialText = 'A new "ExampleStateByDefinition" instance has been created.';
stateText = initialText;
print(stateText);
}
static ExampleStateByDefinition? getState() {
return _instance ?? ExampleStateByDefinition._internal(); //here we use ??
}
}
The above code creates an object each time we call it, so let us look at another variant:
class ExampleStateByDefinition extends ExampleStateBase {
static ExampleStateByDefinition? _instance;
ExampleStateByDefinition._internal() {
initialText = 'A new "ExampleStateByDefinition" instance has been created.';
stateText = initialText;
print(stateText);
}
static ExampleStateByDefinition? getState() {
_instance ??= ExampleStateByDefinition._internal(); //Here we use ??=
return _instance;
}
}
The above code implements Singleton by definition in Design Patterns: Elements of Reusable Object-Oriented Software
The ??= operator is a compound assignment operator.
Just like target += 1 is equivalent to target = target + 1 (but with target only evaluated once, if it's a complicated expression),
target ??= expression is equivalent to target = target ?? expression (but with target only evaluated once and the assignment not even happening if target is non-null).
So, the difference is that the first code probably doesn't work, the second one does.
The code:
return _instance ?? ExampleStateByDefinition._internal();
checks whether _instance is non-null, and if so, it returns the value of _instance. If it is null, it evaluates and returns ExampleStateByDefinition._internal().
No-where in that does it assign to _instance. So, _instance is always going to be null, and the code is probably failing to do what it intended to do—caching a value.
The code:
_instance ??= ExampleStateByDefinition._internal();
return _instance;
or its more streamlined version:
return _instance ??= ExampleStateByDefinition._internal();
will also check if _instance is null and return its value if it isn't.
If _instance is null, it also evaluates ExampleStateByDefinition._internal();, and then it assigns it to _instance, and returns the value.
The next time you come around, _instance will be non-null, and the lazy caching works.
Both ?? and ??= are null-aware operators.
Difference
The difference between the two operators is that the former only evaluates an expression while the latter also assigns the result of the expression.
This is why your first sample returns a new instance each time since you never assign it to your static variable.
??
Use ?? when you want to evaluate and return an expression IFF another expression resolves to null.
The following expression:
exp ?? otherExp;
Is similar to this expression:
((x) => x == null ? otherExp : x)(exp);
??=
Use ??= when you want to assign a value to an object IFF that object is null. Otherwise, return the object.
The following expression:
obj ??= value;
Is similar to this expression:
((x) => x == null ? obj = value : x)(obj);
See Null-aware operators in Dart for reference.
Based on dart tour.
To assign only if the assignment to the variable is null we use ??= operation.
expr1 ?? expr2 if expr1 is not null, return its value; otherwise, evaluates and return the value of expr2.
Cause ?? operation evaluates each time we call the object, it creates a new object.

Dart - Null Operators

I need something like:
if (variable != null) {
otherVariable = variable;
}
Is there a neater way to write this with NullOperators? All I came up with is:
(variable != null) ? otherVariable = variable : null;
There is currently no special operator in Dart which allows an assignment to be dependent on the assigned value being non-null.
The closest operator is ??= where otherVariable ??= variable; does the opposite of what you want: It assigns variable to otherVariable if otherVariable is null, not if variable is non-null.
You could use ?? and go with otherVariable = variable ?? otherVariable;, which assigns the current value of otherVariable back to itself if variable is null. It still performs an assignment in that case, which might be detectable if otherVariable is a setter.
I'd just go with if (variable != null) otherVariable = variable;. It's currently the shortest code which does exactly what you want: Assign to otherVariable only if variable is not null.
This is the shortest way would be :
otherVariable = (variable != null) ? variable : null;
But in your case, if it is null, then why do you assign it 'null' when variable is already null? You could have done this:
otherVariable = variable;
In both the codes same thing happens. Use the Second code as it is short, and use the first one if you have to replace the 'null' by something else like this:
otherVariable = (variable != null) ? variable : 'default';

Using an 'is' expression when the right-hand operand is a variable?

I am trying to write a function that takes two arguments: givenType and targetType. If these two arguments match, I want givenType to be returned, otherwise null.
For this objective, I am trying to utilize Dart's is expression (maybe there is a better way to go about it, I am open to suggestions). Initially, I thought it would be as simple as writing this:
matchesTarget(givenType, targetType) {
if (givenType is targetType) {
return givenType;
}
return null;
}
But this produces an error:
The name 'targetType' isn't a type and can't be used in an 'is'
expression. Try correcting the name to match an existing
type.dart(type_test_with_non_type)
I tried looking up what satisfies an is expression but cannot seem to find it in the documentation. It seems like it needs its right-hand operand to be known at compile-time (hoping this is wrong, but it does not seem like I can use a variable), but if so, how else can I achieve the desired effect?
I cant guess the purpose of the function (or the scenario where it would be used, so if you can clarify it would be great). First of all, I don't know if you are passing "types" as arguments. And yes, you need to specify in compile time the right hand argument of the is function.
Meanwhile, if you are passing types, with one change, you can check if the types passed to your function at runtime.
matchesTarget(Type givenType, Type targetType) {
print('${givenType.runtimeType} ${targetType.runtimeType}');
if (givenType == targetType) {
return givenType;
}
return null;
}
main(){
var a = int; //this is a Type
var b = String; //this is also a Type
print(matchesTarget(a,b)); //You are passing different Types, so it will return null
var c = int; //this is also a Type
print(matchesTarget(a,c)); //You are passing same Types, so it will return int
}
But if you are passing variables, the solution is pretty similar:
matchesTarget(givenVar, targetVar) {
print('${givenVar.runtimeType} ${targetVar.runtimeType}');
if (givenVar.runtimeType == targetVar.runtimeType) {
return givenVar.runtimeType;
}
return null;
}
main(){
var a = 10; //this is a variable (int)
var b = "hello"; //this is also a variable (String)
print(matchesTarget(a,b)); //this will return null
var c = 12; //this is also a variable (int)
print(matchesTarget(a,c)); //this will return int
}
The Final Answer
matchesTarget(givenVar, targetType) {
print('${givenVar.runtimeType} ${targetType}');
if (givenVar.runtimeType == targetType) {
return givenVar;
}
return null;
}
main(){
var a = 10; //this is a variable (int)
var b = String; //this is a type (String)
print(matchesTarget(a,b)); //this will return null because 'a' isnt a String
var c = int; //this is also a type (int)
print(matchesTarget(a,c)); //this will return the value of 'a' (10)
}
The as, is, and is! operators are handy for checking types at runtime.
The is operator in Dart can be only used for type checking and not checking if two values are equal.
The result of obj is T is true if obj implements the interface specified by T. For example, obj is Object is always true.
See the below code for an example of how to use the is operator
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
Even the error message that you're getting says that
The name 'targetType' isn't a type and can't be used in an 'is'
expression.
So the bottomline is that you can use is only for checking if a variable or value belongs to a particular data type.
For checking equality, you can use the == operator if comparing primitive types, or write your own method for comparing the values. Hope this helps!

Why does `if (var = null)` compile in dart?

I've recently came across this question How do I solve the 'Failed assertion: boolean expression must not be null' exception in Flutter
where the problem comes from a should be invalid code that gets treated as valid.
This code can be summarized as :
int stuff;
if (stuff = null) { // = instead of ==
}
But why does this code compiles ? As the following will not.
int stuff;
if (stuff = 42) {
}
With the following compile error :
Conditions must have a static type of 'bool'.
So I'd expect out of consistency that if (stuff = null) to gives the same error.
null is a valid value for a bool variable in Dart, at least until Dart supports non-nullable types.
bool foo = null;
or just
bool foo;
is valid.
Therefore in the first case there is nothing wrong from a statical analysis point of view.
In the 2nd case the type int is inferred because of the assignment, which is known to not be a valid boolean value.
bool foo = 42;
is invalid.
When you say var stuff; with no initial value it is giving stuff a static type of dynamic. Since dyamic might be a bool, it's legal to assign null to a variable of type dynamic, and it's legal to use a possibly null bool in a conditional, the compiler doesn't flag this. When you say int stuff; the compiler knows that stuff could not be a bool. The reported error in that case is cause by the static type of stuff, not the assignment to null.
Edit: Got the real answer from someone who knows how to read the spec.
The static type of an assignment expression is the right hand side of the assignment. So the expression stuff = null has the static type of Null which is assignable to bool.
The reasoning is that the value of an assignment is the right hand side, so it makes sense to also use it's type. This allows expressions like:
int foo;
num bar;
foo = bar = 1;
Commonly assignment operation returns the value that it assigns.
int a = 0;
print(a = 3);//Prints 3
So,
When stuff = null,
'stuff = null' returns null. if statement needs a boolean .null is a sub-Type of boolean.
if(null){}
is valid
When stuff = 42,
'stuff = 42' returns 42. if statement needs a boolean .42 is not a sub-Type of boolean.
if(42){}
is not valid

How can I write custom comparison (definition for binary operator Equal) for entityframework object to an int?

I'm getting this error:
ex = {"The binary operator Equal is not defined for the types 'MySite.Domain.DomainModel.EntityFramework.NickName' and 'System.Int32'."}
What I tried to do was do a select all where the NickNameId = someIntPassedIn... the problem is that the NickNameId is a foreign key, so when it compares the someIntPassedIn to the NickNameId it pulls the whole NickName object that the NickNameId refers to and tries to compare the int to that object.
I need a solution here to allow it to compare the int to the NickName object's Id... so
A) How can I define the binary operator Equal for comparing these two objects
OR
B) How can I compare it directly to the id instead of the whole object?
You don't have to read this, but here's the SelectAllByKey method incase it helps: (I passed in "NickNameId" and "1")
public IList<E> SelectAllByKey(string columnName, string key)
{
KeyProperty = columnName;
int id;
Expression rightExpr = null;
if (int.TryParse(key, out id))
{
rightExpr = Expression.Constant(id);
}
else
{
rightExpr = Expression.Constant(key);
}
// First we define the parameter that we are going to use the clause.
var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
MemberExpression leftExpr = MemberExpression.Property(xParam, this._KeyProperty);
int temp;
BinaryExpression binaryExpr = MemberExpression.Equal(leftExpr, rightExpr);
//Create Lambda Expression for the selection
Expression<Func<E, bool>> lambdaExpr = Expression.Lambda<Func<E, bool>>(binaryExpr, new ParameterExpression[] { xParam });
//Searching ....
IList<E> resultCollection = ((IRepository<E, C>)this).SelectAll(new Specification<E>(lambdaExpr));
if (null != resultCollection && resultCollection.Count() > 0)
{
//return valid single result
return resultCollection;
}//end if
return null;
}
Let me know if you need any more info.
Thanks,
Matt
You should call SelectAllByKey('NickName.ID','1').
Since ID is property of property, you could use this extension method:
public static MemberExpression PropertyOfProperty(this Expression expr,string propertyName)
{
var properties = propertyName.Split('.');
MemberExpression expression = null;
foreach (var property in properties)
{
if (expression == null)
expression = Expression.Property(expr, property);
else
expression = Expression.Property(expression, property);
}
return expression;
}
The accepted answer seems way too complicated for the problem at hand, if I'm reading this correctly.
If I understand you correctly, you're trying to run a query like:
var q = from e in Context.SomeEntities
where e.NickNameId == someIntPassedIn
select e;
...but this won't work, because e.NickNameId is an entity, not an integer.
To reference the Id property, you can just refer to it, like this:
var q = from e in Context.SomeEntities
where e.NickNameId.Id == someIntPassedIn
select e;
Update: If you can't use strong-typed properties due to your level of abstraction (per your comment), then use query builder methods:
var q = (ObjectQuery<T>)Repository.SelectSomething();
return q.Where("it.NickName.Id = " + someIntPassedIn.ToString());
You can adapt this as you see fit, but the general point is that the EF already knows how to translate strings to property members.

Resources