0

I have the following interface:

public interface ISapFunction
  {
    void Import<T>(T obj);
    T Export<T>();
    void Call(RfcRepository repo, RfcDestination dest);
  }

and then I try to implement it as follows:

public class SapMaterialFormatter : ISapFunction
  {
    private static SapMaterialFormatter _self;
    private string _formatted;
    private string _raw;

    private SapMaterialFormatter()
    {
    }

    public void Import<string>(string obj)
    {
     _raw = obj;
    }

    public string Export<string>()
    {
      return _formatted;
    }

    public void Call(RfcRepository repo, RfcDestination dest)
    {
      var bapi = repo.CreateFunction("FUNCTION");
      bapi.SetValue("IF_INPUT", _raw);
      bapi.Invoke(dest);
      _formatted = bapi.GetString("EF_OUTPUT");
    }

    public static SapMaterialFormatter Factory()
    {
      return _self ?? new SapMaterialFormatter();
    }
  }

But the compiler complains, generating syntax errors:

enter image description here

What is wrong with the implementation?

Cœur
  • 37,241
  • 25
  • 195
  • 267
softshipper
  • 32,463
  • 51
  • 192
  • 400

5 Answers5

8

They're generic method parameters, so they need to be provided when you call these interface methods.

For example: impl.Import<string>(...).

Your implementation should just define the whole T generic parameter:

public void Import<T>(T obj)
{

}

If you want the desired effect, you'll need to define a generic type parameter T and remove its method-scoped counterpart:

public interface ISapFunction<T>
  {
    void Import(T obj);
    T Export();
    void Call(RfcRepository repo, RfcDestination dest);
  }
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
2

The implementation is required to implement the open-generic methods, Import<T> and Export<T>, not closed generic methods, such as Import<string> and Export<string>.

The T is not specified by the implementing class, but by the calling site where you call the method.

Example:

var sapFunction = ...;
sapFunction.Import<string>(...);

If you do want the implementing class to specify the type you can declare it in your interface like this:

public interface ISapFunction <T>
  {
    ..
  }

And then implement it:

public class SapMaterialFormatter : ISapFunction<string>
Maarten
  • 22,527
  • 3
  • 47
  • 68
2

Your interface declares a generic method, so your implementation must be generic too:

public class SapMaterialFormatter : ISapFunction
{
    // shortened for brevity

    public void Import<T>(T obj)
    {
         _raw = obj;
    }

    public T Export<T>()
    {
         return _formatted;
    }

    public void Call(RfcRepository repo, RfcDestination dest)
    {
    }
}

If you want to create the derived class with specific types, you may have wanted to declare your interface like that:

public interface ISapFunction<T> // declare generic parameter here
{
    void Import(T obj); // but not here
    T Export();
    void Call(RfcRepository repo, RfcDestination dest);
}

And then declare your class to implement ISapFunction<string>:

public class SapMaterialFormatter : ISapFunction<string>
{
    // shortened for brevity

    public void Import(string obj)
    {
         _raw = obj;
    }

    public string Export()
    {
         return _formatted;
    }

    public void Call(RfcRepository repo, RfcDestination dest)
    {
    }
}
René Vogt
  • 43,056
  • 14
  • 77
  • 99
0

You can choose from two different approaches to achieve generic parameters to methods in interface:

Solution 1.

public interface ISapFunction
{
    void Import<T>(T obj);
    T Export<T>();
}

public class SapMaterialFormatter : ISapFunction
{
    private static SapMaterialFormatter _self;
    private string _formatted;
    private string _raw;

    public static SapMaterialFormatter Factory()
    {
        return _self ?? new SapMaterialFormatter();
    }

    public void Import<T>(T obj)
    {
        throw new NotImplementedException();
    }

    public T Export<T>()
    {
        throw new NotImplementedException();
    }
}

It consists in defining methods as generic both in interface and in implementing class, and it can be used like this:

var sap = new SapMaterialFormatter();
sap.Export<string>();

Solution 2.

public interface ISapFunction<T>
{
    void Import(T obj);
    T Export();
}

public class SapMaterialFormatter : ISapFunction<string>
{
    private static SapMaterialFormatter _self;
    private string _formatted;
    private string _raw;

    public static SapMaterialFormatter Factory()
    {
        return _self ?? new SapMaterialFormatter();
    }

    public void Import(string obj)
    {
        throw new NotImplementedException();
    }

    public string Export()
    {
        throw new NotImplementedException();
    }
}

Which consists in declaring the interface as accepting a generic and using it in the methods.

You can then use it as:

var sap = new SapMaterialFormatter();
sap.Export();

Note: I have removed the content of the methods just to ensure that the code compiles, you are free to change the samples as you want.

meJustAndrew
  • 6,011
  • 8
  • 50
  • 76
0

As everyone already explained to you, you have to make your implementation generic too, you can't specify the type.

The main reason for that is, if I do something like this:

interface ISomething
{
    T Method<T>();
}

class SomethingWithString : ISomething
{
    public string Method<string>()
    {
        return "Hello World !";
    }
}

class SomethingWithInt : ISomething
{
    public int Method<int>()
    {
        return 42;
    }
}

According to you, both of SomethingWithString and SomethingWithInt implement ISomething.

So I could write:

IEnumerable<ISomething> enumerable = new ISomething[]
{
    new SomethingWithString(),
    new SomethingWithInt()
};

And here:

IEnumerable<?> results = enumerable.Select(x => x.Method()); // We may have a problem here...

Except using an object/dynamic type, we can't know the type returned by Method.

This is why you have to change your implementation, others answers already show you how to do that.

romain-aga
  • 1,441
  • 9
  • 14
  • Yeah, i don't think an answer can be: "What everyone else said" unless you really do add some clarity or something new that everyone else missed out – Callum Linington Aug 19 '16 at 13:32
  • Well, I thought it would be interesting to the OP to know why that don't work, everyone is just talking about how to implement it. If everyone, think is not useful, I will delete this. – romain-aga Aug 19 '16 at 13:34
  • @romain-aga Well you can leave there. In my case, I've already explained why it doesn't work... Generic method parameters must be given when the method is called :D – Matías Fidemraizer Aug 19 '16 at 13:39
  • @MatíasFidemraizer I think I should clarify my post, since I'm more in the concept "why this is not allowed", than the "how to implement it". You all have already done that part, no point to do that again. – romain-aga Aug 19 '16 at 13:46
  • That should be better now. What do you think ? – romain-aga Aug 19 '16 at 14:03