Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


C# - reference to a variable?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
12 replies to this topic

#1 Koobazaur   Members   -  Reputation: 691

Like
0Likes
Like

Posted 24 February 2008 - 04:26 PM

I am learning C# right now, coming from C++, and encountering a problem. I have a Form class that has a bitmap called DrawArea that it draws to the screen OnPaint. Further, I have a Renderer object that stores a reference to that DrawArea so it can draw to it. What I do is I pass the DrawArea to my Renderer, by reference, and set it to Renderer's private variable. Thus, the Renderer has a reference that points to the instance of DrawArea. This, however, is NOT what I want. You see, when my window resize, I delete my DrawArea and recreate it. Hence, when that happens, the Renderer's reference points to a, now obsolete, Bitmap*. Is there any way to have a reference point to an actual variable (other reference), rather than an instance in memory? Or do I need to use the "unsafe" pointers? Also can I return a reference via a function? how? (gah, I really dislike the C#'s referencing model ><) Btw, here's my code:
    /// <summary>
    /// This class takes care of rendering out TMEngine components to the screen
    /// </summary>
    class Renderer
    {
        //=========================================================================
        // Public
        //=========================================================================
        
        
        public Renderer(ref Bitmap DrawArea, ref cEntityManager EntityManager, ref cTimer Timer)
            {
            _DrawArea = DrawArea; //this doesn't do what I want
            _rEntityManager = EntityManager;
            _rTimer = Timer;
            
            }
            
            
        public void Update()
            {
                
                Graphics Graph = Graphics.FromImage(_DrawArea); // this throws exception if the DrawArea I passed in constructor gets deleted and re-created

                Pen MyPen = new Pen(Color.Blue); //yaya I know would be more efficient to story this as private member, but optimizations come later

                Graph.DrawEllipse(MyPen, 0, 0, 10, 10);

                MyPen.Dispose();            
            
            }

        //=========================================================================
        // Private 
        //=========================================================================
        private Bitmap _DrawArea;
        private cEntityManager _rEntityManager;
        private cTimer _rTimer;
        
    }


*as a sidenote, I get an exception when I try to modify that bitmap after reference - I thoguht C# did auto-garbage collection and would not delete the bitmap as long as I had some reference pointing to it...

Sponsor:

#2 Telastyn   Crossbones+   -  Reputation: 3726

Like
0Likes
Like

Posted 24 February 2008 - 04:43 PM

I asked this question recently (along with a hackish solution). Though one of the suggestions in there for a wrapper class will work here since the type isn't varying and you don't need closure sort of behavior.


public class KoobazaurBitmap{
public Bitmap Value;
}

public class Renderer{
private KoobazaurBitmap drawingArea;
}

public class Window{
private KoobazaurBitmap drawingArea;
private Renderer internalRenderer;
public Window(){
// create drawingArea;
internalRenderer = new Renderer(drawingArea,...);
}
public void Resize(Rectangle area){
drawingArea.Value = new Bitmap(...with new area...);
// drawingArea remains constant, but its data changes.
}
}



And C# won't collect something if there's a reference to it, so the exception is from something else. C# doesn't have **ptr sort of behavior unfortunately, but once you pickup the patterns and flow of the language that construct doesn't really ever come up.

#3 Koobazaur   Members   -  Reputation: 691

Like
0Likes
Like

Posted 24 February 2008 - 05:51 PM

Hmmm "that construct doesn't ever come up." How then should I have avoided the problem? Is my approach of "The Renderer knows about the surface it draws to" inherently wrong and should be changed to something like "The Renderer queries for the draw surface every render" or even "The Renderer has the draw surface himself" ?

#4 Telastyn   Crossbones+   -  Reputation: 3726

Like
0Likes
Like

Posted 24 February 2008 - 06:02 PM

Without knowing the particulars of your situation or the GDI stuff, I can't say. My renderer doesn't much care about the form beyond resizing (which it listens to via the form's events).

A quick look and it seems the renderer should have a reference to the form (which in turn has a reference to its drawing area) rather than directly to the drawing area.

#5 SnprBoB86   Members   -  Reputation: 277

Like
0Likes
Like

Posted 24 February 2008 - 06:11 PM

accidental double post

#6 SnprBoB86   Members   -  Reputation: 277

Like
0Likes
Like

Posted 24 February 2008 - 06:12 PM

Quote:
really dislike the C#'s referencing model ><)


That's because you are doing it incorrectly. You do not need any of those "ref" keywords that you included.

http://forums.xna.com/thread/40480.aspx
http://msdn2.microsoft.com/en-us/library/14akc2c7(VS.80).aspx

In short: "ref" means to pass the VARIABLE by reference, not the object it points to. All reference types are, automatically, passed by reference. This means that returning a value is also automatically by reference!

#7 Koobazaur   Members   -  Reputation: 691

Like
0Likes
Like

Posted 24 February 2008 - 06:52 PM

SnprBoB86, if you read my original post you will notice that passing the variable by reference is exactly what I wanted, hence I used ref correctly. It's just that I cannot set another variable to reference that variable, only the memory it points to.

And Telastyn, thanks for the feedback. I've changed my renderer to refer to the form as opposed to its drawing area.

Out of curiosity, how did you do the "listening to forms events" thing? In my case, does it simply mean the Form calling my Renderer in the OnResize event, or do you mean something else?

Blargh, maybe I just need to take the time to adjust to the new reference vs value variable model. Sure, maybe C++ is ladden with memory voulnerabilities and unsafe code but, damn, at least when I had ** ppSomething I knew exactly what was going on instead of having to wonder what is referencing what (not to mention extra flexibility). And wtf is up with the lack of template, errr, generic specialization? Seriously...

#8 Telastyn   Crossbones+   -  Reputation: 3726

Like
0Likes
Like

Posted 25 February 2008 - 02:26 AM

Yeah, generic specialization (and template fu in general) is one of the things C++ does have going for it. And adjusting to the reference model does take a little bit of time. After that though it's nice to not have to worry about what level of indirection you're at or if you need to worry about who owns what.

I mean something like that. Conceptually, it's better to think of it as having the Renderer listen/hook onto the Form's Resize (or perhaps Begin or EndResize) event, since Resize will always be triggered and the OOP "who knows what" goes from the Renderer to the Form.


public class Renderer{

// We'll use the odd signature to match the event's
protected void FormResizeHandler( object Form, EventArgs Args ){
// reset Bitmap, handle other stuff
}

public Renderer( Form drawTarget ){
drawTarget.Resize += FormResizeHandler;
}
}




Renderer::FormResizeHandler will now get called whenever the form resizes.

(one thing to note, registering for an event like this provides a reference to a Renderer instance in Form so it won't be collected. You'll need to de-register or else Renderer will stick around as long as form does. Sometimes this is useful, sometimes it leads to a psuedo-memory leak)

#9 JamesLewis   Members   -  Reputation: 234

Like
0Likes
Like

Posted 25 February 2008 - 03:52 AM

Hi guys,

Just had a quick read of your question and came up with this solution:

Instead of storing a Bitmap in the MainForm and a Renderer in the MainForm that references both the MainForm and the Bitmap, why not make some sort of visual renderer component that can sit within the form?

What I mean is, extend the MS PictureBox control (which has an image as a property) and then build your render around it. Then you can just drag this into the MainForm. Also, the PictureBox has an OnResize you can override which will fire when the MainForm is resized. Here's some code:


/// <summary>
/// This class takes care of rendering out TMEngine components to the screen.
/// </summary>
public class Renderer : PictureBox
{
#region Fields
// Entity manager.
private cEntityManager _entityManager = null;

// Timer.
private cTimer _timer = null;
#endregion


#region Properties
/// <summary>
/// This property is used to set / get the entity manager.
/// </summary>
public cEntityManager EntityManager
{
get { return _entityManager; }
set { _entityManager = value; }
}

/// <summary>
/// This property is used to set / get the timer.
/// </summary>
public cTimer Timer
{
get { return _timer; }
set { _timer = value; }
}

/// <summary>
/// This property is used to set / get the draw area.
/// </summary>
public System.Drawing.Bitmap DrawArea
{
get { return this.Image; }
set { this.Image = value; }
}
#endregion

#region Methods
/// <summary>
/// Default constructor.
/// </summary>
public Renderer()
{

}

/// <summary>
/// Constructor used to setup the renderer.
/// </summary>
/// <param name="entityManager">Entity manager.</param>
/// <param name="timer">Timer.</param>
public Renderer(cEntityManager entityManager, cTimer timer)
: this()
{
this.EntityManager = entityManager;
this.Timer = timer;
}

/// <summary>
/// This method is called when the picture box is resized.
/// </summary>
/// <param name="e">Default event arguments.</param>
protected override void OnResize(EventArgs e)
{
// Call the base method
base.OnResize(e);

// Update the renderer
this.Update();
}

/// <summary>
/// Update method.
/// </summary>
public void Update()
{
Graphics Graph = Graphics.FromImage(this.Image);
Pen MyPen = new Pen(Color.Blue);
Graph.DrawEllipse(MyPen, 0, 0, 10, 10);
MyPen.Dispose();
}
#endregion
}



If this isn't what you need then sorry, just thought it might help with the OO side of things!

James

#10 SnprBoB86   Members   -  Reputation: 277

Like
0Likes
Like

Posted 25 February 2008 - 09:40 AM


public Renderer(ref Bitmap DrawArea, ref cEntityManager EntityManager, ref cTimer Timer)
{
_DrawArea = DrawArea; //this doesn't do what I want
_rEntityManager = EntityManager;
_rTimer = Timer;

}



No, this is incorrect usage of "ref". You are not changing DrawArea, EntityManager, or Timer. Remove the ref keyword from all three and you will find that the code behaves 100% identically.

Examine this test code:



using System;
using System.Collections.Generic;

class UsingRef
{

static void AddTwo(int y)
{
y += 2;
}

static void AddTwo(ref int y)
{
y += 2;
}

static void AppendTwo(List<int> b)
{
b.Add(2);
b = null;
}

static void AppendTwo(ref List<int> b)
{
b.Add(2);
b = null;
}

public static void Main()
{

int x = 5;
List<int> a = new List<int>();


AddTwo(x);
Console.WriteLine(x);
AddTwo(ref x);
Console.WriteLine(x);


AppendTwo(a);
Console.WriteLine(a == null ? "null" : a.Count.ToString());
AppendTwo(ref a);
Console.WriteLine(a == null ? "null" : a.Count.ToString());


Console.ReadLine();

}

}



Additionally, a nitpicky comment: your capitalization and naming conventions do not match the standard guidelines. Google-up the .net guidelines.

#11 EdR   Members   -  Reputation: 117

Like
0Likes
Like

Posted 25 February 2008 - 09:42 AM

Quote:
Original post by Koobazaur
Sure, maybe C++ is ladden with memory voulnerabilities and unsafe code but, damn, at least when I had ** ppSomething I knew exactly what was going on instead of having to wonder what is referencing what (not to mention extra flexibility).
Keep in mind how long it took you to understand how that worked, though. C# is preposterously easy and very elegant once it clicks; I literally can't write C++ anymore.



#12 SnprBoB86   Members   -  Reputation: 277

Like
0Likes
Like

Posted 25 February 2008 - 09:49 AM

Quote:
Original post by Koobazaur
at least when I had ** ppSomething I knew exactly what was going on instead of having to wonder what is referencing what (not to mention extra flexibility). And wtf is up with the lack of template, errr, generic specialization? Seriously...


You have nearly the exact same flexibility in C# and you can know "exactly what is going on" with great certainty once you learn the .NET model just as you learned the C++ model.

C# can accomplish non-partial generics specialization through inheritance. For example:

class SpecializedForBar : Foo<Bar>
{
//override virtual members of Foo
}

#13 Shiny   Members   -  Reputation: 456

Like
0Likes
Like

Posted 25 February 2008 - 10:05 AM

       
public void Update()
{

Graphics Graph = Graphics.FromImage(_DrawArea); // this throws exception if the DrawArea I passed in constructor gets deleted and re-created

Pen MyPen = new Pen(Color.Blue); //yaya I know would be more efficient to story this as private member, but optimizations come later

Graph.DrawEllipse(MyPen, 0, 0, 10, 10);

MyPen.Dispose();

}



Just thought I should make a point -- you're clearing up after your pen right here, but not your System.Drawing.Graphics object that you're drawing on -- this is going to be a potential problem later if you're not careful. Anytime you use a graphics object that you didn't get given (i.e., in a paint() method you get given the surface that GDI+ is looking after...) you ought to manually call dispose() so that things don't end up leaking later on :)

This is a fact of life of using GDI+ -- 'tis a C++ library wrapped up in .NET but still must use unmanaged memory for certain stuff (Bitmaps ought to be disposed too, btw).

Anyways, 'twas my two cents. I second the proposition of inheriting from an existing container, too -- save you lots of work. Drawing an Image (system.drawing.image)/Bitmap onto a panel is as easy as grabbing the paint code for said panel and squishing in something along the lines of

Given some PaintEventArgs e -- e.Graphics.DrawImage(Image i);

and voila. DrawImage is overloaded so you can specify things such as height/width/graphics unit etc.

~Shiny




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS