-1

I need following code to work:

class A 
{       
   //Something
}

class Program
{
    static void Main(string[] args)
    {
        A a = new A();
        a[""]("")[""][""]("");
        a[""][""]; // should work as well
    }
}

In other words object should support indexer and operator (). As I know C# does not support operator () for classes. MSDN says that I should use Delegates for that purpose. But what should I do if I need to write code like shown above?

C0x
  • 105
  • 12
  • Do you want to see in your code exactly this pattern? – SergeyS Sep 14 '16 at 13:33
  • You can create an indexer for example like: public int this[int num]{get{...} set{...}} – vmutafov Sep 14 '16 at 13:34
  • Plenty of info on the internet – vmutafov Sep 14 '16 at 13:34
  • 1
    "But what should I do if I need to write code like shown above?" Why do you need code to be written in this way? It is in no way more readable than normal code and doesn't add any functionality. – Lithium Sep 14 '16 at 13:37
  • I know how to write indexer. The problem for me is to support both indexer and operator () for one object. – C0x Sep 14 '16 at 13:39
  • @Lithium This style was implemented in one old library. Now this library does not work in VS2015. I have to rewrite this library code and support same interface. – C0x Sep 14 '16 at 13:43
  • @C0x Surely that library was never implemented or used from C#. I don't know of anything that's changed with the language that would have made this possible. – Kyle Sep 14 '16 at 13:55
  • That library implemented in .net 1.1 and activily used by me in VS 2010 without any problems. But in VS2015 it does not work. You can check it by your self: AutomatedQA.script.dll – C0x Sep 14 '16 at 13:59

4 Answers4

2

Alright, well, this is going to be hairy and I'm going to preface this with a warning that this is generally a bad idea and I strongly recommend just overloading the [] operator. This method causes you to lose virtually all compile-time safety for almost no gain in expressiveness. I imagine the only reason you might be doing this is to save yourself rewriting a bunch of existing code.

We're going to have to rely on the dynamic keyword here. It's the only real way to get this to work.

using System.Dynamic;
public class A : DynamicObject
{
    public dynamic this[string p] => this;

    public override bool TryInvoke( InvokeBinder binder, object[] parameters, out object result )
    {
        result = this;
        return true;
    }
}

Right now this class is basically a no-op. When using the () operator it's actually allowing any number of arguments of any type.

Kyle
  • 6,500
  • 2
  • 31
  • 41
1

I suppose it's just for fun. At least I hope I won't see code like this in production.

Here you have a minimal example which will compile but is essentially a no-op:

class A
{
    public Func<string, B> this [string i] => j => new B();

    public class B
    {
        public A this[string i] => new A();
    }
}

class Program
{
    static void Main(string[] args)
    {
        A a = new A();
        a[""]("")[""][""]("");
    }
}

Here are the types involved in the expression:

  • a is of type A
  • a[""] is of type Func<string, B>
  • a[""]("") is of type B
  • a[""]("")[""] is of type A
  • a[""]("")[""][""] is of type Func<string, B>
  • a[""]("")[""][""]("") is of type B

Ok, you asked for it:

class A : IDynamicMetaObjectProvider
{
    public dynamic this[string i] => this;

    public DynamicMetaObject GetMetaObject(Expression parameter) => new MetaObject(parameter, this);

    private class MetaObject : DynamicMetaObject
    {
        public MetaObject([NotNull] Expression expression, A value)
            : base(expression, BindingRestrictions.GetTypeRestriction(expression, typeof(A)), value)
        {
        }

        public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args) => this;
        public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) => this;
    }
}

static void Main(string[] args)
{
    A a = new A();
    a[""]("")[""][""]("");
    a[""][""](""); // should work as well
}

Needless to say, do not use such tricks in real code please.

Lucas Trzesniewski
  • 50,214
  • 11
  • 107
  • 158
  • 2
    Bear in mind that this implements the exact pattern of indexer and call operators as specified in the question and is not capable of expressing an arbitrary pattern. But this is really the closest you can get with C# (and you probably *really* shouldn't be doing it). – Kyle Sep 14 '16 at 13:54
  • Unfortunately a[""][""] does not work in this case. Requirement is: starting always with [""] and with any random pattern further. – C0x Sep 14 '16 at 14:08
  • @C0x ok you asked for it, I expanded the answer (with an added `("")` to make it a valid C# statement). – Lucas Trzesniewski Sep 14 '16 at 14:40
0

This line:

a[""]("")[""][""]("");
 //^--- Returns a delegate
    //^--- Returns an instance of a class with indexer
        //^--- Returns an instance of a class with indexer
            //^--- Returns a delegate

So this is possible, but it would entirely depend on the definition of your classes

Scott Perham
  • 2,410
  • 1
  • 10
  • 20
0

Your A class should implement indexer operator (How do I overload the square-bracket operator in C#?). That operator can return Func<>, which will allow you to call it as a[key](arg). That function can return another object with indexer and so on.

Community
  • 1
  • 1
kiziu
  • 1,111
  • 1
  • 11
  • 15