问题
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.
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()); }
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...
回答1:
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.
回答2:
The first solution (i.e. no Func) is far simpler. There is absolutely no need to invoke Func objects to implement a garden-variety property accessor.
回答3:
If your interface would end up with just one method it's perfectly valid to use Action
or Func
delegates instead. That's much cleaner imho.
You don't have to write Invoke
though. Just call it with parenthesis:_funcName(_item)
回答4:
Delegates are in many cases going to be more convenient for implementers to use, because both vb.net and C# compilers include features to make it very easy to use them. Interfaces can do some things delegates cannot (e.g. include open generic method families), and may in some cases be more efficient than delegates (since a reference to an object which implements an interface may be passed as that interface type, without having to create another heap object to hold it). The fact that compilers support the easy creation of inline delegates, however, while providing no comparable facility for single-function interfaces, presents a very strong argument in favor of the former.
来源:https://stackoverflow.com/questions/11724411/using-a-func-over-an-interface