Indexers, metadata wise, are simple properties. Only the type is decorated with a special attribute to tell that the property is an indexer. Here's a sample implementation which adds a new Item property that is recognized as an indexer, and delegates to the existing methods.
TypeDefinition type = ...;
// public object get_Item (string str) { ... }
var get_item = new MethodDefinition ("get_Item", MethodAttributes.Public, module.TypeSystem.Object);
set_item.Parameters.Add (new ParameterDefinition ("str", ParameterAttributes.None, module.TypeSystem.String));
type.Methods.Add (get_item);
var il = get_item.Body.GetILProcessor ();
il.Emit (...); // emit call to getObjectViaIndexer
// public void set_Item (string str, object obj) { ... }
var set_item = new MethodDefinition ("set_Item", MethodAttributes.Public, module.TypeSystem.Void);
set_item.Parameters.Add (new ParameterDefinition ("str", ParameterAttributes.None, module.TypeSystem.String));
set_item.Parameters.Add (new ParameterDefinition ("obj", ParameterAttributes.None, module.TypeSystem.Object));
type.Methods.Add (set_item);
il = set_item.Body.GetILProcessor ();
il.Emit (...); // emit call to setObjectViaIndexer
// public object this [string str] { ... }
var item = new PropertyDefinition ("Item", PropertyAttributes.None, module.TypeSystem.Object) {
HasThis = true,
GetMethod = get_item,
SetMethod = set_item,
};
type.Properties.Add (item);
// [DefaultMemberAttribute("Item")]
var default_member = new CustomAttribute (module.Import (typeof (System.Reflection.DefaultMemberAttribute).GetConstructor (new [] { typeof (string) })));
default_member.ConstructorArguments.Add (new CustomAttributeArgument(module.TypeSystem.String, "Item"));
type.CustomAttributes.Add (default_member);
Of course you could also just create a property which points to the existing methods, as long as you have the proper attribute.