Why can I ref return an item of an array that only exists inside the method? - ref

I was trying out the new ref returns of C#7.
I am able to compile and build this:
public ref string MisUseRefReturn(int index)
{
string[] array = { "a", "b", "c", "d" };
return ref array[index]; //array[2] gets out of scope when this method returns!
}
According to MSDN: The return value cannot be a local variable in the method that returns it; it must have a scope that is outside the method that returns it. It can be an instance or static field of a class, or it can be an argument passed to the method. Attempting to return a local variable generates compiler error CS8168, "Cannot return local 'obj' by reference because it is not a ref local."
So why does this compile? When I execute this method, the returned reference shows the correct string.

Think of the array element as if it were an instance field of the array. Imagine your array were:
public class FourElementStringArray
{
public string element0;
public string element1;
public string element2;
public string element3;
}
Then your code is equivalent to:
public ref string MisUseRefReturn(int index)
{
var array = new FourElementStringArray
{
element0 = "a",
element1 = "b",
element2 = "c",
element3 = "d"
};
// TODO: do this dynamically based on index
return ref array.element2;
}
That abides by the piece of documentation you quoted:
It can be an instance or static field of a class, or it can be an argument passed to the method.
Is this useful? Not particularly. Is it dangerous? No.
The latter part is the important point. Using the regular array, if the caller assigns a new value, that's fine - it's replacing an element of an array, on the heap. The array can't be garbage collected while the array element reference exists; all is basically okay.
What the compiler rule is trying to prevent is the use of a returned reference to somewhere on the stack which has then been popped by the method returning. The array itself isn't on the stack, so there's no problem here.

See the description here:
Returning a local int variable instead of an array is not possible.
int is a value type, and thus the variable gets out of scope at the
end of the method, and thus a reference to it cannot be returned.
That’s different with an array. An array is a reference type, and the
array is allocated on the heap. An int within the array can be
returned with the ref keyword.
You could not return an integer value directly as it is a value type. An array element can be returned because it is a reference type. The MSDN statement is correct.
... of an array that only exists inside the method
The returned reference persists the array beyond the method call.

Related

Passing Object.Attr to function

Usually, a reference to an Object's Attribute Value returns that value.
Object o = current Object
display o."Object Text"
However, if I pass that reference to a function that expects a string parameter, I get an error.
string displaySomeString(string s) {
display s
}
Object o = current Object
displaySomeString(o."Object Text")
I get this result from the debugger:
-E- DXL: incorrect arguments for function (displaySomeString)
-I- DXL: All done. Errors reported: 1. Warnings reported: 0.
What gives? How do I robustly pass an Attribute value into a function?
Here's my suspicion. If you're passing the object attribute value directly in the function call--
displaySomeString(o.attr)
--instead try passing it with an empty string at the end:
displaySomeString(o.attr "")
Or set the attribute value as something like
string v = o.attr
and then pass v in as
displaysomeString(v)
and I think it might work. o.attr really isn't a string, but a derived type, and concatenating an empty string at the end casts it to a string.

Swift: Dictionary passed as parameter is always empty

I want to fill Dictionary via a method of which dictionary is one of the parameter. When I add a value to key, say 15, it always return 0 count when I try to access it second time with same key i.e. 15. Here's the code.
private static var showWidgetMap = Dictionary<Int, [BaseWidget]>()
private static var hideWidgetMap = Dictionary<Int, [BaseWidget]>()
static func initHandler()
{
let cloudWidget = CloudWidget()
cloudWidget.setType(CreatorConstants.CLOUD)
let property1 = [CreatorConstants.IMG_SRC: "cloud1", CreatorConstants.X_COORD: "100", CreatorConstants.Y_COORD: "450"]
cloudWidget.setPropery(property1)
addWidgetInLocalTimeList(15, widget: cloudWidget, delete: false)
let emojiWidget = CloudWidget()
emojiWidget.setType(CreatorConstants.EMOTICON)
let property2 = [CreatorConstants.IMG_SRC: "1", CreatorConstants.X_COORD: "100", CreatorConstants.Y_COORD: "550"]
emojiWidget.setPropery(property2)
addWidgetInLocalTimeList(15, widget: emojiWidget, delete: false)}
static func addWidgetInLocalTimeList(time_milisec: Int, widget: BaseWidget, delete: Bool)
{
if(delete)
{
checkAndAdd(hideWidgetMap, key: time_milisec, widget: widget);
}
else
{
checkAndAdd(showWidgetMap, key: time_milisec, widget: widget);
}
}
private static func checkAndAdd(var map: Dictionary<Int, [BaseWidget]>, key: Int, widget: BaseWidget)
{
print("map count is")
print(map.count)
if var val = map[key]
{
val.append(widget);
}
else
{
var temp: [BaseWidget] = [];
temp.append(widget);
map[key] = temp
print(map.count)
}
}
print(map.count) always returns 0.
You need to understand the difference between value types and reference types.
Value type variables are just values. For example, an array is a value type. It is just a value of "a bunch of stuff" *. On the other hand, reference types are references to values. For example, when you create a UIViewController, that variable actually stores a reference to the actual UIviewController *.
Don't really understand? Then it's analogy time! The variables and constants you create are children. The things you put in variables and constants are balloons.
There are two types of children, one type (value types) likes to hold balloons directly in their hands. The other type (reference types) likes to hold balloons using a string **.
When you pass a child to a method, depending on what type of child he is, different things will happen:
A value type child holds the balloon in his hands, so tightly that the method parameter can't take it away from him. So what can it do? It creates a copy of it! It then takes the copy to the method implementation let it do its thing.
A reference type, however, holds balloons using a string. The method parameter will tie another string to the balloon so the implementation can access it using the string. As a result, no copies of the balloon are created.
So what are you doing wrong here?
Since swift dictionaries are value types, when you pass a dictionary to a method, as I said above, it creates a copy! In the implementation, you are actually editing a copy of the dictionary, not the original one. That's why the original dictionary still has a count of 0.
What can you do?
Instead of marking the parameter with var, which is a very bad practice btw, you mark it with inout!
private static func checkAndAdd(inout map: Dictionary<Int, [BaseWidget]>, key: Int, widget: BaseWidget)
The inout modifier basically says
Hey parameter, next time you see a value type, just get a string and tie it to the balloon that the child is holding.
There is also another thing that you should do. That is you should change the way you call your method.
Instead of
checkAndAdd(showWidgetMap, key: time_milisec, widget: widget)
You write
checkAndAdd(&showWidgetMap, key: time_milisec, widget: widget)
And magically, it works!
Conclusion: Parameters are dumb. They aren't even smart enough to know when to tie a string. Be careful when you work with value types.
Footnotes * Assume it is not nil
** Not the String type, but an actual string.

"Cannot subscript a value of type '[String]' with an index of type 'String'

I'm trying to display a dynamically updated array in a label using:
for i in result {
outputLbl.text = result[i].joinWithSeparator("\n")
}
However I get the error
Cannot subscript a value of type '[String]' with an index of type 'String'.
Any idea how I can fix this?
Note that when using the loop "header" for X in Y, you don't get the indices of Y, but the actual elements of Y. Judging from your error, results is an array of strings ([String]). Hence, i in you for loop represents---one by one---the elements in the String array results.
So, if you wanted to access the string elements one by one in the for loop above, you could use the approach in your example:
let result = ["Hello", "World"]
for myString in result {
// use each string element in some manner...
}
However, as you are using the array method joinWithSeparator(..), you should use, just as Leo writes in his comment above, this method directly on your array (and not their elements!)
let result = ["Hello", "World"]
outputLbl.text = result.joinWithSeparator("\n")
/* Hello\nWorld */
I think what you are doing here is trying to iterate through an array of string and then update a label that is in this case "outputLbl".Here you can do something like this
for i in result {
//result is array of strings.
// here i is individual element of result array
/* outputLbl.text = result[i].joinWithSeparator(“\n”)*/
//instead you can write
outputLbl.text = i.joinWithSeparator(“\n”)
}
The reason you are getting this error is as follows:
It seems you are confusing the type of i. The variable result is of type [String] which means it is an array of String types. By virtue of being an array, it must be subscripted with an Int, not a String. So something like result[0] or result[12] is valid, but something like result["hello"] is not valid. The variable i here is a String because it is a single element in an array of String types, which means that effectively, what you're trying to do by saying result[i] is something along the lines of result["hello"].
That having been said, the true solution to your problem is that the method joinWithSeparator(_:String) is not a String method but rather a Sequence type method. Which means it should be called on a Sequence like an object of type [String]. So what you should use is:
outputLbl.text = result.joinWithSeparator("\n")
What's going on here is the compiler is inferring i to be of type String since that's what result is. You should be more verbose in your naming conventions.
for specificString in result {
outputLbl1.text += "\n\(specifcString)"
}
EDITED for correctness.

I am creating objects with var because I mutate them but I get warning: "Variable 'variableName' was never mutated, consider..."

I am creating at launch Dictionaries with var because I will modify them later when user does something. Dictionaries are added inside an Array in a singleton class to be used in multiple places but I get the warning "Variable 'variableName' was never mutated, consider...."
in the place I am creating them
If I make them with let and when I get object form array to modify it if I take it from array with var, no crash, no warning, no nothing...
What is the explanation for this?
UPDATE:
My Singleton Class:
class Config {
static let sharedInstance = Config()
var array_shapes: Array<Dictionary<NSObject,AnyObject>> = Array()
func createInitialShapeArray(){
var avion = createShapeDictionaryFor("Avion", objectName: "Avion", badgeStatus: "0", shapeImageName: "shape_avion");
//.......more objects like avion
array_shapes = [avion,//.....the other objects];
}
func createShapeDictionaryFor(objectID:String, objectName:String, badgeStatus:String, shapeImageName:String) -> Dictionary<NSObject,AnyObject>{
var dict: Dictionary<NSObject,AnyObject> = [:]
dict["objectID"] = objectID
dict["objectName"] = objectName
dict["badgeStatus"] = badgeStatus
dict["shapeImageName"] = shapeImageName
return dict;
}
}
And when I am mutating dictionaries (In main class):
#IBAction func btnPressed_done(sender:UIButton){
pennyPincherGestureRecognizer.recognize();
screenShotMethod()
var dict = Config.sharedInstance.array_shapes[Config.sharedInstance.currentShapeIndex] as Dictionary<NSObject,AnyObject>
dict["badgeStatus"] = "1"
self.initNextShape()
}
var avion has the warning "Variable 'variableName' was never mutated, consider...."
It is not an error trough, it's a warning and I was curious if I could silence them or what can I do to make them dissappear
Facts
You are declaring avion as a local variable of the method createInitialShapeArray
You are not mutating avion in the scope where it is defined
avion is a Dictionary therefore a Struct (value type rules are applied)
Conclusion
There is no need to declare avion as a variable, it should be a constant.
Please note that where you write
array_shapes = [avion, ...]
you are creating a copy of avion (because it's a Dictionary).
So if you change the value inside array_shapes you are changing another value.
Therefore, at the end of the day, you are not mutating avion... and the compiler is right, it should be a constant.
Example
Please consider the following code
func foo() {
var dict = [1: "One"] // <-- Compiler warning
var anotherDict = dict
anotherDict[2] = "Two"
}
Here I am getting the same compiler warning
Variable 'dict' was never mutated; consider changing to 'let' constant
This happens because I am changing anotherDict that is not just another reference to the same value, it is actually a totally different value. This is the rule with Struct(s) and Enum(s) because they are Value Types.
Hope this helps.
In Swift arrays and dictionaries are declared as struct so when you pass them to other function or use them in assignments their value is copied and not passed as reference the same way it's done for classes, this means that when you pass avion to the append() function of your array you pass a copy of the dictionary so the original variable is never mutated.
The same things happens when you try to modify on dictionary in the array thus copying the dictionary of your interest in dict: you aren't modifying the array inside your shared instance but the local variable dict.

Object.ReferenceEquals returns incorrect results (in Silverlight 3 at least)

I just discovered a very strange behaviour. I have a class with a string property. In the setter of this property I compare the old value with the new value first and only change property if the values differ:
set
{
if ((object.ReferenceEquals(this.Identifier, value) != true))
{
this.Identifier = value;
this.RaisePropertyChanged("Identifier");
}
}
But this ReferenceEquals almost always returns false! Even if I call object.ReferenceEquals("test", "test") in Quick Watch I get false.
How is this possible?
That's because strings are immutable in C#:
The contents of a string object cannot
be changed after the object is
created, although the syntax makes it
appear as if you can do this.
Since you can't modify an existing string reference, there's no benefit in reusing them. The value passed to your property setter will always be a new string reference, except maybe if you do this.Identifier = this.Identifier;.
I'll try to clarify with an example:
string s = "Hello, "; // s contains a new string reference.
s += "world!"; // s now contains another string reference.

Resources