2009-02-12 12 views
7

Я пытаюсь выяснить способ автоматически бросить что-то действие или Func и лучшее, что я могу придумать что-то вроде этого:Почему я не могу использовать метод делегата с расширением?

[TestFixture] 
public class ExecutionTest 
{ 
    public void BadMethod() 
    { 
     throw new Exception("Something bad happened"); 
    } 

    [Test] 
    public void TestBadMethod() 
    { 
     // Want this, but it won't work!! 
     // BadMethod.Execute().IgnoreExceptions(); 

     // Ick 
     ((Action)BadMethod).Exec().IgnoreExceptions(); 

     // Still ick 
     ((Action)BadMethod).IgnoreExceptions(); 

     // Do not want 
     ExtensionMethods.Exec(BadMethod).IgnoreExceptions(); 

     // Better but still meh 
     this.Exec(BadMethod).IgnoreExceptions(); 

    } 
} 

public static class ExtensionMethods 
{ 
    public static Action Exec(this Action action) 
    { return action; } 

    public static Action Exec(this object obj, Action action) 
    { return action; } 

    public static void IgnoreExceptions(this Action action) 
    { 
     try { action(); } 
     catch {} 
    } 
} 

Там должен лучше/легкий способ сделать это, какие-то мысли?

ответ

5

В C#, когда вы используете имя метода без круглых скобок, оно называется группой методов и не имеет представления, кроме времени компиляции. Группа методов может представлять более одного метода (из-за перегрузок и переопределений), поэтому для неявного определения того, какой метод необходим, должен быть указан целевой тип делегата.

В вашем случае вам интересно, почему тип параметра метода расширения не вызывает разрешение функции. Проще говоря, расширение оценивается после того, как тип известен, т. Е. Этот параметр не может использоваться как цель неявного преобразования.

Пример того, почему он сломается:

class Test 
{ 
    void M (void) // Fits Action delegate 
    { 
    } 

    int M (int) // Fits Func<int,int> delegate 
    { 
     return 5; 
    } 

    void Test() 
    { 
     M.Exec(); // UHOH!!! Which Exec to resolve to ??? 
    } 
} 


public static class Extensions 
{ 
    public static void Exec(this Action action) { } 
    public static void Exec(this Func<int, int> func) { } 
} 

Как вы можете видеть, есть конфликт, но на самом деле, конфликт никогда не происходит потому, что C# не будет даже пытаться найти соответствие расширение с помощью группы методов.

Обратите внимание, как это не будет работать:

class A 
{ 
    public static implicit operator int (A a) 
    { 
     return 5; 
    } 

    void F() 
    { 
     A a = new A(); 
     a.Blah(); // Error! It won't implicitly try C.Blah() 
    } 
} 

public static class C 
{ 
    public static void Blah (int i) 
    { 
    } 
} 

C# не будет соответствовать A к C.Blah(int), поскольку это потребует неявного преобразования.

+0

Не уверен, что это проблема перегрузок метода, потому что компилятор определит, какой из них назвать. Но это лучший ответ, который я видел до сих пор ... –

2

Как говорит Коинкоин, это не будет хорошо работать на C# из-за чрезмерной любви к перегрузке метода. Единственный способ решения проблемы я видел, что люди используют, чтобы создать Action и Func методы:

public Action Action(Action f) { return f; } 
public Action<A> Action<A>(Action<A> f) { return f; } 
... 
public Func<A,B,C,D,E> Func(Func<A,B,C,D,E> f) { return f; } 

Вы могли бы даже назвать их все «F», чтобы получить какой-то короткий синтаксис:

F(BadMethod).NoExceptions(); 

Вы могли бы решите не определять эти методы в своем классе и помещать их в служебную программу Funcs или что-то в этом роде. Алиас его с F и она не заканчивается слишком плохо:

F.F(BadMethod).NoException(); 

Но в целом это все еще сосет :(

+0

Да, именно поэтому я метод расширения для (этого объекта Object, Action action), поэтому он будет работать внутри любого класса, вызвав this.Action() или this.Func() –

+0

Извините, я не видел t определение Exec - несколько вводящее в заблуждение имя, нет: P – MichaelGG

+0

Да, я переименовал его в свой фактический код. В то время это казалось «бегло». –

1

F # позволяет делать такие вещи очень естественно, обеспечивая гораздо более умозаключение типа.