Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a Func<> over an interface?

I have an already existing generic class

public class Foo<T>
{
    private T _item;
    public Foo(T item){ _item = item;}
}

I have to create a method which will return a certain property of T.
I see two solutions here.

  1. Creating an interface :

    public const string TestVar = "bar";
    public interface INamable { string Name { get; } }
    public class Bar : INamable
    {
        public string Name { get { return TestVar; } }
    }
    
    public class Foo<T> where T : INamable
    {
        private T _item;
        public Foo(T item) { _item = item; }
        public string GetName() { return this._item.Name; }
    }
    
    [TestMethod()]
    public void TestInterface()
    {
        var bar = new Bar();
        var foo = new Foo<Bar>(bar);
        Assert.AreEqual(TestVar, foo.GetName());
    }
    
  2. Passing a Func :

    public const string TestVar = "bar";
    public class Bar
    {
        public string Name { get { return TestVar; } }
    }
    
    public class Foo<T>
    {
        private T _item;
        private Func<T, string> _funcName;
        public Foo(T item, Func<T, string> funcName) { _item = item; _funcName = funcName; }
        public string GetName() { return _funcName.Invoke(_item); }
    }
    
    [TestMethod()]
    public void TestFunc()
    {
        var bar = new Bar();
        var foo = new Foo<Bar>(bar, b => b.Name);
        Assert.AreEqual(TestVar, foo.GetName());
    }
    

I go with the second solution, since I don't have to create an interface, and I'm lazy. Just have to add one parameter to the already call of the foo constructor.

Moreover, the Foo class can still be used with all sort of class, and I prefer it this way.

BUT, I'm not sure if it is the good way to use a Func? Is it still nice and SOLID? This is my first try to use a Func, that's why I'm asking myself!

A couple of months ago I used the first solution, with classic interface...

like image 855
Cyril Gandon Avatar asked Jul 30 '12 15:07

Cyril Gandon


People also ask

What is Func <> C#?

Func is a delegate that points to a method that accepts one or more arguments and returns a value. Action is a delegate that points to a method which in turn accepts one or more arguments but returns no value. In other words, you should use Action when your delegate points to a method that returns void.

How do I inherit an interface in TypeScript?

Interfaces and Inheritance An interface can be extended by other interfaces. In other words, an interface can inherit from other interface. Typescript allows an interface to inherit from multiple interfaces. Use the extends keyword to implement inheritance among interfaces.

Can interface contain operators?

You can't define operators on interfaces because a class can implement multiple interfaces.


1 Answers

I'm re-phrasing my answer, because from the comments I realized I haven't been very clear.

It depends on the intent of your code and on who has the responsibility to give a name to your classes (the client should know and specify how to name the classes vs the classes should inherently have a Name). i'm changing @Scorpi0's example a little, so that Foo actually does something with the name (it greets it) instead of just returning it.

Example 1 (interfaces)

interface INamable { string Name { get; } }

class AThingWithAName : INamable 
{
    public string Name {get {return "thing";}}
}

class AnotherThingWithAName : INamable 
{
    public string Name {get {return "different thing";}}
}

class Foo<T> where T : INamable
{
    public string Greet(T item) {return "hi " + item;}
}

Here Things have a Name regardless of Foo. Foo knows how to Greet Things as long as they have a Name (which is guaranteed by contract by them implementing the INamable interface). Here you want to guarantee that Foo only works on things with a Name and attempts to greet anything else should be a type error caught by the compiler.

Also, note that you're encapsulating the concrete implementation of Name in each class. Name could depend on its class's private state.

Example 2 (Func)

class AThing {}
class AnotherUnrelatedThing {}

class Foo<T>
{
    public string Greet(T item, Func<T, string> namingFunction) 
    {
        return "hi " + namingFunction(item);
    }
}

Here the emphasis is on Foo. Things don't necessarily have a Name. I want to build a class Foo that can greet anything. How does it do that? Well, it's the caller's responsibility, for any Type, to pass in a function that can give a name to that type. In this case, not only we're not requesting a guarantee that T knows its own Name, but we're building Foo in a way that it can Greet any type, as long as WE know how to give it a name. For instance this works:

var foo = new Foo<int>();
var res=foo.Greet(2, n=>n.ToString());

In exchange for that flexibility, we're giving up encapsulation. Now Name is no longer something that is the class's responsibility to implement, we (the caller) are telling Foo how to give a name to each class we use it with.

So, depending on what you want to express and if you have a hierarchy of objects that have a name regardless of Foo, you may choose one or the other way.

like image 186
Paolo Falabella Avatar answered Sep 23 '22 00:09

Paolo Falabella