Neden var'a anonim bir yöntem atanamıyor?


140

Takip koduna sahibim:

Func<string, bool> comparer = delegate(string value) {
    return value != "0";
};

Ancak, aşağıdakiler derlenmez:

var comparer = delegate(string value) {
    return value != "0";
};

Derleyici bunun neden olduğunu anlayamıyor Func<string, bool>? Bir string parametresi alır ve bir boolean döndürür. Bunun yerine, bana hata veriyor:

Örtük olarak yazılan bir yerel değişkene anonim yöntem atanamıyor.

Ben bir tahmin var ve bu var sürümü derlenmiş ise , eğer aşağıdaki vardı tutarlılık eksikliği olurdu:

var comparer = delegate(string arg1, string arg2, string arg3, string arg4, string arg5) {
    return false;
};

Func <> yalnızca 4 bağımsız değişkene izin verdiğinden (Mantık 3.5, kullandığım budur) yukarıdakiler mantıklı olmaz. Belki birisi sorunu açıklığa kavuşturabilirdi. Teşekkürler.


3
.NET 4'teki 4 bağımsız değişken bağımsız değişkeniniz hakkında Func<>en fazla 16 bağımsız değişken kabul edilir.
Anthony Pegram

Açıklama için teşekkürler. .NET 3.5 kullanıyorum.
Marlon

9
Derleyicinin bunun bir olduğunu düşündürmesini niye Func<string, bool>? Converter<string, bool>Bana a benziyor !
Ben Voigt


3
bazen VB özledim ..Dim comparer = Function(value$) value <> "0"
Slai

Yanıtlar:


155

Diğerleri zaten kastetmiş olabileceğiniz sonsuz sayıda olası delege türü olduğunu belirtmişlerdir ; bu kadar özel Funcolan şey, Predicateya Actionda başka bir olasılık yerine varsayılan olmayı hak ediyor mu? Ve lambdalar için niyetin ifade ağacı formundan ziyade delege formunu seçmek olduğu açıktır?

Ama bunun Funcözel olduğunu söyleyebiliriz ve bir lambda veya anonim yöntemin çıkarılan türü bir şeyin Func'udur. Hala her türlü sorunumuz var. Aşağıdaki durumlar için ne tür çıkarımlar yapmak istersiniz?

var x1 = (ref int y)=>123;

Func<T>Ref hiçbir şey alan bir türü yoktur .

var x2 = y=>123;

Resmi parametrenin türünü bilmiyoruz, ancak dönüşü biliyoruz. (Yoksa biz mi? Dönüş int? Uzun? Kısa? Byte?)

var x3 = (int y)=>null;

Dönüş türünü bilmiyoruz, ancak geçersiz olamaz. Dönüş türü, herhangi bir referans türü veya boş değerli bir değer türü olabilir.

var x4 = (int y)=>{ throw new Exception(); }

Yine, dönüş tipini bilmiyoruz ve bu sefer geçersiz olabilir .

var x5 = (int y)=> q += y;

Bu geçersiz dönen bir lambda mı yoksa q'ya atanan değeri döndüren bir şey mi? Her ikisi de yasal; hangisini seçmeliyiz?

Şimdi, bu özelliklerin hiçbirini desteklemediğinizi söyleyebilirsiniz. Sadece türlerin çalışılabileceği "normal" vakaları destekleyin. Bu yardımcı olmuyor. Bu hayatımı nasıl kolaylaştırıyor? Özellik bazen çalışıyor ve bazen başarısız olursa o zaman hala tüm bu hata durumlarını tespit etmek ve her biri için anlamlı bir hata mesajı vermek için kod yazmak zorunda . Hala tüm bu davranışları belirtmemiz, belgelememiz, testler yazmamız vb. Bu bir kullanıcıyı belki yarım düzine tuşa basarak kurtaran çok pahalı bir özelliktir . Dile değer katmanın daha iyi yolları var, yarım çalışmayan ve çalıştığı durumlarda neredeyse hiç fayda sağlamayan bir özellik için test senaryoları yazmak için çok zaman harcamak.

Aslında yararlı olduğu durum:

var xAnon = (int y)=>new { Y = y };

çünkü o şey için "konuşulabilir" tip yoktur. Ancak her zaman bu problemimiz var ve sadece türü çıkarmak için yöntem türü çıkarımını kullanıyoruz:

Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; }
...
var xAnon = WorkItOut((int y)=>new { Y = y });

ve şimdi yöntem türü çıkarımı işlev türünün ne olduğunu ortaya çıkarır.


44
SO cevaplarınızı ne zaman bir kitapta derleyeceksiniz? Ben satın alırdım :)
Matt Greer

13
SO cevaplarının Eric Lippert kitabı önerisini ikinci olarak verdim. Önerilen başlık: "Yığından Yansımalar"
Adam Rackis

24
@Eric: İyi cevap, ancak bunu D'de tamamen iyi çalıştığı için bunu mümkün olmayan bir şey olarak göstermek biraz yanıltıcı. Sadece siz delege değişmezlerine kendi türlerini vermeyi seçmediniz ve bunun yerine onları bağımlı kıldınız. onların bağlamında ... IMHO'nun cevabı "çünkü biz bunu böyle yaptık" şeklinde olmalı. :)
user541686

5
@abstractdissonance Derleyicinin açık kaynak olduğunu da not ediyorum. Bu özelliği önemsiyorsanız, bunun gerçekleşmesi için gerekli zaman ve çabayı bağışlayabilirsiniz. Çekme isteği göndermenizi öneririm.
Eric Lippert

7
@AdstractDissonance: Maliyeti sınırlı kaynaklar açısından geliştirdik: geliştiriciler ve zaman. Bu sorumluluk tanrı tarafından verilmemiştir; geliştirici bölümünün başkan yardımcısı tarafından empoze edildi. C # ekibinin bir şekilde bir bütçe sürecini görmezden gelebileceği fikri gariptir. Sizi temin ederim, C # topluluklarının isteklerini dile getiren uzmanları, Microsoft'un stratejik misyonunu ve tasarımdaki mükemmel lezzetlerini göz önünde bulunduran uzmanların dikkatli ve düşünceli bir şekilde dikkate alınmasını sağlarız.
Eric Lippert

29

Sadece Eric Lippert kesin olarak biliyor, ancak bence delege tipinin imzası, türü benzersiz bir şekilde belirlemiyor.

Örneğinizi düşünün:

var comparer = delegate(string value) { return value != "0"; };

İşte olması gerekenler için iki olası çıkarım var:

Predicate<string> comparer  = delegate(string value) { return value != "0"; };  // okay
Func<string, bool> comparer = delegate(string value) { return value != "0"; };  // also okay

Derleyici hangisini çıkarmalı? Birini veya diğerini seçmek için iyi bir neden yok. Ve a Predicate<T>işlevsel olarak a'ya eşdeğer olsa da , Func<T, bool>.NET türü sistem düzeyinde hala farklı türlerdir. Bu nedenle derleyici delege türünü net bir şekilde çözemez ve tür çıkarımında başarısız olmalıdır.


1
Eminim Microsoft'taki diğer birkaç kişi de emin. ;) Ama evet, başlıca bir nedenden bahsediyorsunuz, derleme zamanı türü belirlenemiyor çünkü hiçbiri yok. Dil spesifikasyonunun 8.5.1 Bölümü, anonim işlevlerin örtülü olarak yazılan değişken bildirimlerinde kullanılmasına izin vermemek için bu nedeni özellikle vurgulamaktadır.
Anthony Pegram

3
Evet. Ve daha da kötüsü, lambdalar için delege tipine gidip gitmediğini bile bilmiyoruz; bir ifade ağacı olabilir.
Eric Lippert

Herkes ilgi için ben daha fazla bu ve hakkında biraz nasıl C # ve F # de kontrastı yaklaşır yazdım mindscapehq.com/blog/index.php/2011/02/23/...
itowlson

neden derleyici lambda işlevi için C ++ gibi yeni bir benzersiz tür
üretemiyor

".NET tipi sistem düzeyinde" ne fark gösterir?
arao6

6

Eric Lippert'in söylediği yerde eski bir yazısı var

Ve aslında C # 2.0 belirtimi bunu söyler. Yöntem grubu ifadeleri ve anonim yöntem ifadeleri C # 2.0'da yazımsız ifadelerdir ve lambda ifadeleri C # 3.0'da bunlara katılır. Bu nedenle, örtük bir bildirimin sağ tarafında "çıplak" görünmeleri yasadışıdır.


Ve bu, dil spesifikasyonunun 8.5.1 bölümü ile vurgulanmıştır. Örtük olarak yazılan bir yerel değişken için kullanılabilmesi için "başlatıcı ifadesinin bir derleme zamanı türüne sahip olması gerekir".
Anthony Pegram

5

Farklı delegeler farklı türler olarak kabul edilir. örneğin, Actionbundan farklıdır MethodInvokerve bir Actiontür değişkenine bir örneği atanamaz MethodInvoker.

Yani, isimsiz bir temsilci (veya lambda) gibi () => {}, bir Actionveya bir MethodInvoker? Derleyici bunu söyleyemez.

Benzer şekilde, bir temsilci türünü stringargüman alıp geri döndürürsem bool, derleyici Func<string, bool>delege türüm yerine gerçekten istediğinizi nasıl bilebilir ? Delege türünü çıkartamaz.


2

Aşağıdaki noktalar, örtük olarak yazılan yerel değişkenler ile ilgili MSDN'den alınmıştır:

  1. var yalnızca yerel bir değişken aynı ifadede bildirildiğinde ve başlatıldığında kullanılabilir; değişken null değerine, yöntem grubuna veya anonim işleve başlatılamaz.
  2. Var anahtar sözcüğü derleyiciye değişken türünü başlatma deyiminin sağ tarafındaki ifadeden çıkarması talimatını verir.
  3. Var anahtar kelimesinin "varyant" anlamına gelmediğini ve değişkenin gevşek yazıldığını veya geç bağlı olduğunu göstermediğini anlamak önemlidir. Bu sadece derleyicinin en uygun türü belirlemesi ve ataması anlamına gelir.

MSDN Başvurusu: Örtük Olarak Yazılan Yerel Değişkenler

Anonim Yöntemlerle ilgili olarak aşağıdakileri dikkate alarak:

  1. Anonim yöntemler, parametre listesini atlamanızı sağlar.

MSDN Başvurusu: Anonim Yöntemler

Anonim yöntemin aslında farklı yöntem imzalarına sahip olabileceğinden, derleyicinin atamak için en uygun türün uygun şekilde çıkarımda bulunamadığından şüphelenirim.


1

Yayımım asıl soruya cevap vermiyor, ancak asıl soruya cevap veriyor:

"Böyle çirkin bir tür yazmak zorunda kalmamak nasıl olur Func<string, string, int, CustomInputType, bool, ReturnType>?"[1]

Olduğum tembel / hileli programcı olarak, Func<dynamic, object> tek bir girdi parametresi alıp bir nesneyi döndüren - .

Birden çok argüman için bunu şu şekilde kullanabilirsiniz:

dynamic myParams = new ExpandoObject();
myParams.arg0 = "whatever";
myParams.arg1 = 3;
Func<dynamic, object> y = (dynObj) =>
{
    return dynObj.arg0.ToUpper() + (dynObj.arg1 * 45); //screw type casting, amirite?
};
Console.WriteLine(y(myParams));

İpucu: Kullanabilirsiniz Action<dynamic> Bir nesneyi döndürmeniz gerekmiyorsa kullanabilirsiniz.

Evet, muhtemelen programlama ilkelerinize aykırı olduğunu biliyorum, ama bu bana ve muhtemelen bazı Python kodlayıcılarına mantıklı geliyor.

Delegelerde oldukça acemi oldum ... sadece öğrendiklerimi paylaşmak istedim.


[1] Bu Func, parametre olarak önceden tanımlanmış bir yöntem gerektirmediğini varsayar; bu durumda, o çirkin dizeyi yazmanız gerekir: /


0

Bu nasıl?

var item = new
    {
        toolisn = 100,
        LangId = "ENG",
        toolPath = (Func<int, string, string>) delegate(int toolisn, string LangId)
        {
              var path = "/Content/Tool_" + toolisn + "_" + LangId + "/story.html";
              return File.Exists(Server.MapPath(path)) ? "<a style=\"vertical-align:super\" href=\"" + path + "\" target=\"_blank\">execute example</a> " : "";
        }
};

string result = item.toolPath(item.toolisn, item.LangId);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.