Local variable not initializing in C# - c#

I have a static function that initializes a list of tiles:
private static bool MyFunction(SomeObj obj)
{
List<Tile> tiles = obj.Tile.GetTilesBetween(otherTile);
// do stuff
}
Later in MyFunction(), I initialize a collection using LINQ:
{
// we did stuff
List<Commands> commands = CommandsOccuringInTimespan(obj.timeElapsed);
IEnumerable<Commands> match = commands.Where(c => tiles.Contains(c.End));
// do more stuff
}
However, the presence of these lines, specifically the initialization of match, is causing tiles to either not initialize or to initialize but not be available in the Debugger. I can't understand why this would be happening.
I can seem to make it work if I make a copy of tiles and use that when initializing match:
// we did stuff
List<Commands> commands = CommandsOccuringInTimespan(obj.timeElapsed);
List<Tile> copyOfTiles = new List<Tile>(tiles);
IEnumerable<Commands> match = commands.Where(c => copyOfTiles.Contains(c.End));
// do more stuff
}
C# 7 (I think; this is a Unity3D app so I believe that's the current supported version).

Try adding the .ToList() method to tiles:
List<Tile> tiles = obj.Tile.GetTilesBetween(otherTile).ToList();

Related

Roslyn Check If Field Declaration has been assigned to

I'm writing an app which converts keys to use resources from a RESX File. This code was working with local variables before:
public static void AnalyzeConstDeclaration(SyntaxNodeAnalysisContext context)
{
var fieldDeclaration = (FieldDeclarationSyntax)context.Node;
if (false == IsValidFieldDeclaration(context, fieldDeclaration))
{
return;
}
var firstVariable = fieldDeclaration.Declaration.Variables.FirstOrDefault();
var dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(firstVariable);
var variableSymbol = context.SemanticModel.GetDeclaredSymbol(firstVariable);
if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
{
return;
}
var firstSymbol = context.SemanticModel.GetDeclaredSymbol(firstVariable);
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation(), firstSymbol.Name));
}
However when I try to get the dataFlowAnalysis I receive an error:
Additional information: statementOrExpression is not a StatementSyntax or an ExpressionSyntax.
How can Ideally just need to see if anyone has written to this variable outside of the declaration.
DataFlow works by analyzing order of execution within a single method.
It doesn't make sense for class-level fields.
Instead, you should use a simple syntax visitor (or SymbolFinder) to search the entire class for assignments to the field.
You'll probably also want to check whether it's ever passed as a ref parameter.

How to evaluate local variable/ parameter state with Roslyn

I have a bit of complicated situation. I must create analyzers/ code fix providers for situations such as a parameter is only assigned but never used or local variable are never used.
For the parameter situation, I'm going for the method declaration and looking at the parameter list to get all the analyzer. I'm going through assignment expressions within the method and I filter the parameters that were assigned with an helper method.
Where it gets fuzzy is I have no clue or to know when a local variable/parameter is used or not. I've gone through symbols but they can't tell me that variable used/ not used. I could try to find how many times a variable's name was mentioned inside a method by turning the method declaration syntax context in a string and look for the parameters that were assigned but that's simply such a BAD idea.
I'm really stuck and I would some help for this from anyone who had previous experience with this kind of situation.
For people who might ask, I'm mostly looking for the missing logic for the analyzer. I have no idea how the code fix provider will work. If you have an idea of what I could do, feel free to include it in your answer ! As of now, I was thinking that a local variable that's not used could be deleted from a method and the same could go for an unused parameter. I'm not sure at the moment.
UPDATE
I'm now trying to use the DataFlow API but it's not working for me at the moment. The oldest answer of this thread gave me a starting point but it's actually not working.
I came up with my own way :
private static bool IsLocalVariableBeingUsed(VariableDeclaratorSyntax variableDeclarator, SyntaxNodeAnalysisContext syntaxNode)
{
var model = syntaxNode.SemanticModel.Compilation.GetSemanticModel(variableDeclarator.SyntaxTree);
var methodBody = variableDeclarator.AncestorsAndSelf(false).OfType<MethodDeclarationSyntax>().First();
var lastMethodNode = methodBody?.ChildNodes().LastOrDefault();
if (lastMethodNode == null)
return false;
var readWrite = syntaxNode.SemanticModel.AnalyzeDataFlow(variableDeclarator, lastMethodNode);
}
But this also is not working. When using a test with NUnit :
var input = #"
class TestClass {
void TestMethod ()
{
int i;
}
}";
I get the following message when the runtime gets to either readWrite or result(from oldest answer):
System.ArgumentOutRangeException Index was out of range Must be non negative and lesser than the size of the collection"
But before that in my analyzer, when I try to validate my node to make sure it's not null and create the appropriate elements for the data flow API, there's no code break (not sure if that is the appropriate term) but at the moment I cannot progress.
You can see whether or not most variable are used (read/written) via the DataFlowAnalysis APIs. I've written an introduction to this API on my blog.
I believe in your case, you're looking for variables that are never read.
var tree = CSharpSyntaxTree.ParseText(#"
public class Sample
{
public void Foo()
{
int unused = 0;
int used = 1;
System.Console.Write(used);
}
}");
var Mscorlib = PortableExecutableReference.CreateFromAssembly(typeof(object).Assembly);
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree }, references: new[] { Mscorlib });
var model = compilation.GetSemanticModel(tree);
var methodBody = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single().Body;
DataFlowAnalysis result = model.AnalyzeDataFlow(methodBody);
var variablesDeclared = result.VariablesDeclared;
var variablesRead = result.ReadInside.Union(result.ReadOutside);
var unused = variablesDeclared.Except(variablesRead);
foreach(var variable in unused)
{
Console.WriteLine(variable);
}
Building on JoshVarty's answer, to get this to work in a diagnostic, I would register a SyntaxNodeAction for all MethodDeclaration Syntax Kinds and then look inside the body for unused variables:
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeIt, SyntaxKind.MethodDeclaration);
}
private static void AnalyzeIt(SyntaxNodeAnalysisContext context)
{
var method = context.Node as MethodDeclarationSyntax;
var dataFlow = context.SemanticModel.AnalyzeDataFlow(method.Body);
var variablesDeclared = dataFlow.VariablesDeclared;
var variablesRead = dataFlow.ReadInside.Union(dataFlow.ReadOutside);
var unused = variablesDeclared.Except(variablesRead);
if (unused.Any())
{
foreach (var unusedVar in unused)
{
context.ReportDiagnostic(Diagnostic.Create(Rule, unusedVar.Locations.First()));
}
}
}

Use variable value to check if particular instance exists, if not - create it

I have a list of images like this:
public List<Image> imageList = new List<Image>();
I also have a picture class in order to collect and manipulate data about the images in the list:
public Class Pic {
// properties and stuff
}
And then I have a function that takes an integer as an argument. That integer corresponds to an image in the image list. What I want to do in the function is to check if an instance of the Pic class has been created for that particular image. If not, I want to create it, using the value of the variable passed into the function. The following code obviously doesn't work, but it shows what I want:
public void doStuffWithImage(int picNumber) {
// Check if instance called pic + picNumber exists
if(pic + picNumber.toString() == null) {
// Create an instance
Pic pic + picNumber.toString() = new Pic();
}
}
Suggestions on how to accomplish this?
It seems like you're trying to create individual variables pic1, pic2, etc. you'd be better off using a dictionary:
Dictionary<int, Pic> pics = new Dictionary<int, Pic>();
public void doStuffWithImage(int picNumber) {
// Check if instance called pic + picNumber exists
if(!pics.ContainsKey(picNumber)) {
// Create an instance
pics[picNumber] = new Pic();
}
}
You need to create a "registry" of known Pics. DIctionary<int,Pic> would be good collection to hold this registry. You need to store the registry itself somewhere - perhaps in the "factory" object that registers your pictures.
class PicFactory {
private readonly IDictionary<int,Pic> knownPics = new Dictionary<int,Pic>();
public Pic GetOrCreate(int id) {
Pic res;
if (knownPics.TryGetValue(id, out res)) {
return res;
}
res = new Pic(id.ToString()); // This assumes that Pic(string) exists
knownPics.Add(id, res);
return res;
}
}
This way of implementing a registry may be too primitive for your purposes - for example, if you need your registry to be concurrent, you would need to set up some sort if a locking scheme to protect the knownPics dictionary. The class that accesses pictures would need to create an instance of PicFactory, so that it could access pictures through the GetOrCreate(id) call.
If you are using .net 4.0 or more you can use Lazy type which:
Provides support for lazy initialization.
Which means that the object will be constructed not in the moment of declaration, but when first accessed.
So you can basically declare them like
List<Lazy<Pic>> ....
See Lazy<T> and the Lazy Loading Pattern in general - this is actually a common optimization technique as it defers what can add up to a lot at startup to microdelays during runtime.
Be wary about making sure the microdelays are worth it, and I advise leaving methods about which can force loading.
If you're grabbing from a list, preface with a .Any or .Contains check, and since you're looking up by name like that, consider using a Dictionary instead

Creating an array of objects using initializers seems to fail

I have a GameObject we'll call the GM. Attached to it is a script that's meant to be the primary logical controller for a game.
In that script, somewhere, I have:
private dbEquipment equipment_database = new dbEquipment();
The relevant snippet from dbEquipment.cs:
public class dbEquipment {
private int total_items = 13;
private clEquipment[] _master_equipment_list;
public dbEquipment() {
_master_equipment_list = new clEquipment[total_items];
_master_equipment_list[0] = new clEquipment {
... //large amount of object initializing here
};
... //etc, for all 13 items
}
}
When I run Unity, I get:
NullReferenceException: Object reference not set to an instance of an object
Pointed at the line:
_master_equipment_list[0] = new clEquipment { ...
I tried running through the array and initializing every clEquipment object to an empty clEquipment() first:
for(int x = 0; x < total_items; x++) { _master_equipment_list[x] = new clEquipment(); }
just to be totally sure that the array was actually filled, but I got the same result.
I've also tried changing it to be a List<clEquipment>, and changing everything appropriately -- no dice.
Any ideas?
My guess is that you may been including a null reference in the section that says //large amount of object initializing here when you create a new clEquipment.
_master_equipment_list[0] = new clEquipment {
... //check for nulls here
};
You might want to post the code for the clEquipment class. You say you tried initializing every object...did you do that before the break line? If it didn't break, thats a good sign.
Also, hard to tell from your code, but do you need a "()" in the initialization where it breaks? Just a thought
_master_equipment_list[0] = new clEquipment () {

How can I add an object to a collection without knowing it's type?

I added a "console" in my game, in which I can type commands, and receive a response (it's based on this). I need to be able to access objects that I instantiate in my Game.cs in my console class. I can't pass them to the constructor, since I don't know how much there will be once the "engine" is done.
I tried using a method to add the objects to a Dictionnary<string, object> but I can't access the properties.
What I'd like to be able to do:
Game1.cs
TileMap tileMap = new TileMap();
BakuConsole console;
...
console = new BakuConsole(this, Content.Load<SpriteFont>("ConsoleFont"));
console.AddRef("tileMap", tileMap);
BakuConsole.cs
public void AddRef(/* some args */) {
// halp!
}
public void Execute(string input) {
switch (input)
{
case "some --command":
console.WriteLine(g.ToString());
// execute stuff, with an object that was added via AddRef()
console.WriteLine("");
console.Prompt(prompt, Execute);
break;
default:
console.WriteLine("> " + input + " is not a valid command.");
console.WriteLine("");
console.Prompt(prompt, Execute);
break;
}
}
I hope I am clear enough. Thanks!
Edit:
I just don't want my constructor to grow too big in case I add more types:
TileMap tileMap = new TileMap();
OtherType1 ot1 = new OtherType1();
OtherType2 ot2 = new OtherType2();
OtherType3 ot3 = new OtherType3();
OtherType4 ot4 = new OtherType4();
OtherType5 ot5 = new OtherType5();
IronPython does exactly what I want to do, and does it via Globals.Add("string", object"). However, I can't seem to find it in the source (of IronPython).
From what you described, you don't actually need a dictionary, you need several properties on some object, probably directly on BakuConsole;
class BakuConsole
{
… // your current code here
public TileMap TileMap { get; set; }
public OtherType1 OtherType1 { get; set; }
…
}
Then, you can set it like this:
console.TileMap = tileMap;
console.OtherType1 = otherType1;
…
And then when you use it, you won't have any problems with accessing properties.
I can see that you are passing a reference of Game to the Console class. Why don't you use that reference to access what you need from the Game class?
You neeed to specify the class name in dictionary of which object you are going to add in dictionary.
Suppose, I had to add "Game1" objects,then we should initialize dictionary in following way...
Dictionary<string, Game1> dicDemo = new Dictionary<string, Game1>();
Game1 objgame1 = new Game1();
dicDemo.Add(string.Empty,objgame1);

Categories

Resources