Author Topic: A C# Event Primer (for iago!)  (Read 21461 times)

0 Members and 1 Guest are viewing this topic.

Offline MyndFyre

  • Boticulator Extraordinaire
  • x86
  • Hero Member
  • *****
  • Posts: 4540
  • The wait is over.
    • View Profile
    • JinxBot :: the evolution in boticulation
Re: A C# Event Primer (for iago!)
« Reply #15 on: November 06, 2007, 12:35:57 pm »
Fine, since you promised. These are just the things that bug me most.

My least favorite thing so far is pointers, though. Having to declare variables as "fixed" indicates a fundamental problem: they're trying to add a low-level paradigm to a high-level language, and end up with ugly constructs like that. It doesn't have a good feeling to me.

I also hate operator overloading. To me, a + should always add and a << should always left-shift. C# provides operator overloading, which is something I can get over. However, when using, for example, delegates, you add all your events to your delegate with + and remove them with -. That kind of thing just isn't right, to me. Then when you want to find out if it's empty, you compare it to null. That's just confusing, it took me awhile to figure out how a non-reference variable could be null. In my opinion, it's exactly why operator overloading is bad.

Another thing I don't like is that they .. damnit, I got an email and left for 10 minutes, so I totally forget what I was writing. Ohwell. :)
Muwahahahaha, I didn't promise!

For the record, C# pointers don't always work with the fixed keyword.  They only are used when you need to obtain a pointer to a heap-allocated, garbage-collected object.  Arrays are heap-allocated except when using the stackalloc keyword:
Code: [Select]
unsafe void PointMe()
{
    byte* ptr = stackalloc byte[80];
}
Stack-allocated memory is intrinsically cleaned up at the end of a function scope.  The fixed keyword exists to inform the garbage collector not to automatically relocate an object during a garbage collection pass.  As I've gone farther in my C# use, I do what I can to avoid using fixed in favor of other constructs (Marshal.AllocHGlobal, Marshal.AllocCoTaskMem, and stackalloc - as far as I'm concerned, Marshal.AllocHGlobal and AllocCoTaskMem are the same as using malloc).

For operator overloading, best practices indicate that the overloaded operators should be used only for the mathematical functions they represent.  For instance, MBNCSUtil's BigInteger has overloaded operators for all of its numeric functions.  Best practices further dicate that there should be named functions (such as Add()) for any overloaded operators.

As for delegates and events, I suppose it's a matter of preference:
Code: [Select]
this.btnOK.Click += new EventHandler(this.btnOK_Click);
// vs.
this.btnOK.addActionListener(new ButtonActionListener(this));
To me, += and .addXXX() pretty much equate to the same thing.  I think of += as syntactic sugar - I can say
Code: [Select]
Delegate.Join(btnOK.Click, new EventHandler(btnOK_Click));

And you're mistaken about delegates and events - they *are* reference types.  That's why you need to check for nullility.

I agree with you - I don't like e-mail.  Particularly work things are notorious for pulling me away from important things like posting on the forums.  I think we should make e-mail illegal immediately!
I have a programming folder, and I have nothing of value there

Running with Code has a new home!

Our species really annoys me.

Offline Camel

  • Hero Member
  • *****
  • Posts: 1703
    • View Profile
    • BNU Bot
Re: A C# Event Primer (for iago!)
« Reply #16 on: November 06, 2007, 01:07:13 pm »
I've got to agree with iago. In Java, the delegates issue is solved by storing a List of a particular type of interface. Additionally, you can declare anonymous classes, which means you can do cool things like this:

Code: [Select]
tray.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON1) {
setVisible(!isVisible());
if(isVisible())
toFront();
}
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
});

The implementation code is quite elegant:
Code: [Select]
public class SomethingWithEvents {
List<EventHandlingInterface> delegates = new ArrayList<EventHandlingInterface>();

public boolean addDelegate(EventHandlingInterface ehi) {
return delegates.add(ehi);
}

private void doEvent(...) {
for(EventHandlingInterface ehi : delegates)
ehi.eventName(...);
}
}

I think MyndFyre said something about this coming to .NET in 3.0?

<Camel> i said what what
<Blaze> in the butt
<Camel> you want to do it in my butt?
<Blaze> in my butt
<Camel> let's do it in the butt
<Blaze> Okay!

Offline Camel

  • Hero Member
  • *****
  • Posts: 1703
    • View Profile
    • BNU Bot
Re: A C# Event Primer (for iago!)
« Reply #17 on: November 06, 2007, 01:10:44 pm »
And you're mistaken about delegates and events - they *are* reference types.  That's why you need to check for nullility.

What? In Java, an empty list is not null, they are a reference to a List object that happens to have no contents. I think this is what iago was pointing out.

<Camel> i said what what
<Blaze> in the butt
<Camel> you want to do it in my butt?
<Blaze> in my butt
<Camel> let's do it in the butt
<Blaze> Okay!

Offline iago

  • Leader
  • Administrator
  • Hero Member
  • *****
  • Posts: 17914
  • Fnord.
    • View Profile
    • SkullSecurity
Re: A C# Event Primer (for iago!)
« Reply #18 on: November 06, 2007, 01:13:08 pm »
Muwahahahaha, I didn't promise!
Luckily, it's mostly preference stuff, so I can't really argue any of your points. Plus, I don't like the idea of being on the same side as Camel. :)

But it's really hard to explain, things just feel hackish to me. *shrug*

Offline Camel

  • Hero Member
  • *****
  • Posts: 1703
    • View Profile
    • BNU Bot
Re: A C# Event Primer (for iago!)
« Reply #19 on: November 06, 2007, 01:21:11 pm »
Plus, I don't like the idea of being on the same side as Camel. :)

That's a wise position to take.

<Camel> i said what what
<Blaze> in the butt
<Camel> you want to do it in my butt?
<Blaze> in my butt
<Camel> let's do it in the butt
<Blaze> Okay!

Offline MyndFyre

  • Boticulator Extraordinaire
  • x86
  • Hero Member
  • *****
  • Posts: 4540
  • The wait is over.
    • View Profile
    • JinxBot :: the evolution in boticulation
Re: A C# Event Primer (for iago!)
« Reply #20 on: November 06, 2007, 01:39:58 pm »
I've got to agree with iago. In Java, the delegates issue is solved by storing a List of a particular type of interface. Additionally, you can declare anonymous classes, which means you can do cool things like this:

Code: [Select]
tray.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON1) {
setVisible(!isVisible());
if(isVisible())
toFront();
}
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
});
See, the difficulty there is that you're still creating an entire type to handle a signal, when a function will do.

C# 2.0 (2005) introduced anonymous methods so that function definitions wouldn't need to be class members:
Code: [Select]
btnClickMe.Click += delegate { MessageBox.Show("*GASP!* You clicked me!"); };
Anonymous methods also support parameters:
Code: [Select]
btnClickMe.MouseDown += delegate(object sender, MouseEventArgs e) { MessageBox.Show(string.Format("({0}, {1})", e.X, e.Y)); };

The implementation code is quite elegant:
Code: [Select]
public class SomethingWithEvents {
List<EventHandlingInterface> delegates = new ArrayList<EventHandlingInterface>();

public boolean addDelegate(EventHandlingInterface ehi) {
return delegates.add(ehi);
}

private void doEvent(...) {
for(EventHandlingInterface ehi : delegates)
ehi.eventName(...);
}
}

I think MyndFyre said something about this coming to .NET in 3.0?
This has actually been part of .NET since the beginning.  A little-known (because it's really not used much) feature of C# (and actually, VB too) is that you can manually provide event backing fields for events:
Code: [Select]
private EventHandler handlers;
public event EventHandler Clicked
{
    add(EventHandler eh)
    {
        Delegate.Combine(this.handlers, eh);
    }
    remove(EventHandler eh)
    {
        Delegate.Remove(this.handlers, eh);
    }
}
Invocation is then done:
Code: [Select]
if (handlers != null)
  handlers(this, EventArgs.Empty);

Though, I still think that from an OO standpoint, the fact that an object posesses properties and events, and that other objects listen to those events by registering for them, is more clear than registration via a behavior (which is what a method is supposed to represent - a behavior).

Incidentally, this is (more or less) what the compiler does when you create:
Code: [Select]
public event EventHandler Clicked;
I have a programming folder, and I have nothing of value there

Running with Code has a new home!

Our species really annoys me.

Offline Camel

  • Hero Member
  • *****
  • Posts: 1703
    • View Profile
    • BNU Bot
Re: A C# Event Primer (for iago!)
« Reply #21 on: November 06, 2007, 09:45:47 pm »
See, the difficulty there is that you're still creating an entire type to handle a signal, when a function will do.

If your quarrel is with the bulky code, I agree with you. However, your concerns are mostly unwarranted because hotspot will optimize this down beyond an anonymous method if possible. I say mostly because hotspot does this optimization at runtime, not compile time. The bytecode in the class files still has the extra info.

There's no way to say that either Java or C# is inherently better than the other, however, the preference always seems to come back to the same thing: what OO means to you. In Java, it means everything is an instance of an <Object>. This makes the language intrinsically simpler to learn, and, in my opinion, more elegant.

<Camel> i said what what
<Blaze> in the butt
<Camel> you want to do it in my butt?
<Blaze> in my butt
<Camel> let's do it in the butt
<Blaze> Okay!

Offline MyndFyre

  • Boticulator Extraordinaire
  • x86
  • Hero Member
  • *****
  • Posts: 4540
  • The wait is over.
    • View Profile
    • JinxBot :: the evolution in boticulation
Re: A C# Event Primer (for iago!)
« Reply #22 on: November 06, 2007, 10:37:13 pm »
There's no way to say that either Java or C# is inherently better than the other, however, the preference always seems to come back to the same thing: what OO means to you. In Java, it means everything is an instance of an <Object>. This makes the language intrinsically simpler to learn, and, in my opinion, more elegant.
But see, in Java, everything *isn't* an instance of <Object>.
Code: [Select]
int i = 5;

i isn't an Object.  It is in C#, though! :P
I have a programming folder, and I have nothing of value there

Running with Code has a new home!

Our species really annoys me.

Offline Joe

  • B&
  • Moderator
  • Hero Member
  • *****
  • Posts: 10319
  • In Soviet Russia, text read you!
    • View Profile
    • Github
Re: A C# Event Primer (for iago!)
« Reply #23 on: November 07, 2007, 12:13:32 am »
Agreed!

Java is, syntactically, beautiful. As for its implementation, well, that's another story.

You missed something: Java and C# syntax are 99.9% the same. I've copy/pasted hundreds of lines of my Java code into C#, fixed the API references, and then ran them with zero syntax conversion.
I'd personally do as Joe suggests

You might be right about that, Joe.


Offline Camel

  • Hero Member
  • *****
  • Posts: 1703
    • View Profile
    • BNU Bot
Re: A C# Event Primer (for iago!)
« Reply #24 on: November 07, 2007, 12:31:29 am »
There's no way to say that either Java or C# is inherently better than the other, however, the preference always seems to come back to the same thing: what OO means to you. In Java, it means everything is an instance of an <Object>. This makes the language intrinsically simpler to learn, and, in my opinion, more elegant.
But see, in Java, everything *isn't* an instance of <Object>.
Code: [Select]
int i = 5;

i isn't an Object.  It is in C#, though! :P

Java will automatically cast an int to an <Integer> if you try to use it as an object, and you're splitting hairs.

Agreed!

Java is, syntactically, beautiful. As for its implementation, well, that's another story.

You missed something: Java and C# syntax are 99.9% the same. I've copy/pasted hundreds of lines of my Java code into C#, fixed the API references, and then ran them with zero syntax conversion.

I was clearly talking about corner cases: enums, anonymous types, delegates, etc.
« Last Edit: November 07, 2007, 12:33:56 am by Camel »

<Camel> i said what what
<Blaze> in the butt
<Camel> you want to do it in my butt?
<Blaze> in my butt
<Camel> let's do it in the butt
<Blaze> Okay!

Offline MyndFyre

  • Boticulator Extraordinaire
  • x86
  • Hero Member
  • *****
  • Posts: 4540
  • The wait is over.
    • View Profile
    • JinxBot :: the evolution in boticulation
Re: A C# Event Primer (for iago!)
« Reply #25 on: November 07, 2007, 12:47:58 am »
Java will automatically cast an int to an <Integer> if you try to use it as an object, and you're splitting hairs.
You still need to allocate a heap object.  It's not so much a splitting-hairs thing so much as a "here's a big difference between the runtimes."
I have a programming folder, and I have nothing of value there

Running with Code has a new home!

Our species really annoys me.

Offline Chavo

  • x86
  • Hero Member
  • *****
  • Posts: 2219
  • no u
    • View Profile
    • Chavoland
Re: A C# Event Primer (for iago!)
« Reply #26 on: November 07, 2007, 11:55:29 pm »
Speaking of split hairs, I'm thinking about trying a new shampoo.  What do you think?

Offline Camel

  • Hero Member
  • *****
  • Posts: 1703
    • View Profile
    • BNU Bot
Re: A C# Event Primer (for iago!)
« Reply #27 on: November 08, 2007, 12:08:19 pm »
Java will automatically cast an int to an <Integer> if you try to use it as an object, and you're splitting hairs.
You still need to allocate a heap object.  It's not so much a splitting-hairs thing so much as a "here's a big difference between the runtimes."

I don't know how .NET handles this situation; could you enlighten me?

FWIW though, your point seems to be moot, since all objects in all languages exist in the heap. Maybe I missed your point?

<Camel> i said what what
<Blaze> in the butt
<Camel> you want to do it in my butt?
<Blaze> in my butt
<Camel> let's do it in the butt
<Blaze> Okay!

Offline MyndFyre

  • Boticulator Extraordinaire
  • x86
  • Hero Member
  • *****
  • Posts: 4540
  • The wait is over.
    • View Profile
    • JinxBot :: the evolution in boticulation
Re: A C# Event Primer (for iago!)
« Reply #28 on: November 08, 2007, 01:04:10 pm »
FWIW though, your point seems to be moot, since all objects in all languages exist in the heap. Maybe I missed your point?
Hmm, maybe you don't know how all languages work?

In .NET, value-types (everything declared with the struct keyword instead of the class keyword, as well as enums) live on the stack instead of on the heap.  The .NET stack, like the x86 stack, is a place for temporary variables.  So let's say I have a class that holds 12 bytes of information; we'll push the stack by 12 bytes and say that the memory space between that push is where the variable lives.  When a function is called referencing the variable, the variable is copied to the new stack.  So for example:
Code: [Select]
// C#
public struct Point
{
  public int X, Y;
}
public class CPoint
{
  public int X, Y;
}
void Test()
{
  Point p = new Point() { X = 10, Y = 20 };
  ModifyPoint(p);
  Console.WriteLine("({0}, {1})", p.X, p.Y);
  CPoint cp = new CPoint() { X = 15, Y = 30 };
  ModifyPoint(cp);
  Console.WriteLine("({0}, {1})", cp.X, cp.Y);
}
void ModifyPoint(Point p)
{
  p.X *= 2;
  p.Y *= 2;
}
void ModifyPoint(CPoint p)
{
  p.X *= 2;
  p.Y *= 2;
}

The output of this is:
Code: [Select]
(10, 20)
(30, 60)

This is similar to C and C++:
Code: [Select]
typedef struct tagPOINT
{
  int X;
  int Y;
} Point;
void ModifyPoint(Point p)
{
  p.X *= 2;
  p.Y *= 2;
}
void ModifyPPoint(Point *p)
{
  p->X *= 2;
  p->Y *= 2;
}
void ModifyRPoint(Point& p)
{
  p.X *= 2;
  p.Y *= 2;
}
Now, C++ gives you a little more flexibility:
Code: [Select]
Point p; // allocates 8 bytes on the stack
Point* p = new Point; // allocates 8 bytes on the heap and the machine pointer size on the stack
You can use all 3 modify-point functions on the first one (since you can get a pointer to a stack variable as well), but you can only use the ModifyPPoint function on the heap object.

Incidentally, this is the way Java treats primitive types too.  You just can't expect them to change when they get back to the original function.  If I have an int in Java, and I call toString() on it, the compiler implicitly creates a new Integer object on the heap and calls the function on that.  IMO that's a bit of a hack.  OTOH, .NET calls .ToString() directly on the int on the stack.
I have a programming folder, and I have nothing of value there

Running with Code has a new home!

Our species really annoys me.

Offline Camel

  • Hero Member
  • *****
  • Posts: 1703
    • View Profile
    • BNU Bot
Re: A C# Event Primer (for iago!)
« Reply #29 on: November 08, 2007, 02:02:12 pm »
Objects are instances of classes. In most languages, they are created with the new keyword (which is equivalent to a malloc() call plus a call to the constructor), and therefore can only exist on the stack as a pointer to the heap.

Java's elegance comes from its purity. There speed advantage of keeping an object on the stack (avoiding references to "far" memory locations) is so negligible that it isn't worth justifying in such a high level language. KISS is a founding principle of Java. :)

<Camel> i said what what
<Blaze> in the butt
<Camel> you want to do it in my butt?
<Blaze> in my butt
<Camel> let's do it in the butt
<Blaze> Okay!