The equivalent is to use arrays of controls. You will have to add the Index in the event manually. If you don't need the index, you can just assign the events to the controls.
A downside is, both for C# and VB.NET: you cannot create indexed controls with the designer, you have to create them by hand.
The upside is: this gives you more freedom.
EDIT:
This is how that looks:
// in your Form_Init:
Button [] buttons = new Button[10]; // create the array of button controls
for(int i = 0; i < buttons.Length; i++)
{
buttons[i].Click += new EventHandler(btn_Click);
buttons[i].Tag = i; // Tag is an object, not a string as it was in VB6
// a step often forgotten: add them to the form
this.Controls.Add(buttons[i]); // don't forget to give it a location and visibility
}
// the event:
void btn_Click(object sender, EventArgs e)
{
// do your thing here, use the Tag as index:
int index = (int) ((Button)sender).Tag;
throw new NotImplementedException();
}
PS: if you use the form designer, each button will have its own name. If you do as another user suggested, i.e., use the same handler for all of them (as you could do in VB6 too), you cannot easily distinguish the controls by index, as you used to do. To overcome this, simply use the Tag field. It's usually better not to use the Name field for this, as this creates a dependency you don't want.