Tasarım zamanında var kullanılarak bildirilen bir değişkenin türünü güvenilir bir şekilde nasıl belirleyebilirim?


109

Emacs'de C # için bir tamamlama (intellisense) tesisi üzerinde çalışıyorum.

Buradaki fikir şudur, eğer bir kullanıcı bir parça yazarsa ve daha sonra belirli bir tuş vuruşu kombinasyonu yoluyla tamamlama isterse, tamamlama tesisi olası tamamlamaları belirlemek için .NET yansımasını kullanır.

Bunu yapmak, tamamlanan şeyin türünün bilinmesini gerektirir. Bir dizeyse, bilinen bir dizi olası yöntem ve özellik vardır; bir Int32 ise, ayrı bir kümesi vardır ve bu böyle devam eder.

Emacs'de bulunan bir kod lexer / ayrıştırıcı paketi olan semantik kullanarak, değişken bildirimlerini ve türlerini bulabilirim. Buna göre, türdeki yöntemleri ve özellikleri almak için yansımayı kullanmak ve ardından seçenekler listesini kullanıcıya sunmak kolaydır. (Tamam, emacs içinde yapmak pek kolay değil , ancak emacs içinde bir powershell işlemi çalıştırma yeteneğini kullanarak çok daha kolay hale geliyor. Yansıtma yapmak, powershell'e yüklemek için özel bir .NET derlemesi yazıyorum ve sonra içinde çalışan elisp emacs, powershell'e komutlar gönderebilir ve yanıtları comint aracılığıyla okuyabilir. Sonuç olarak emacs yansıma sonuçlarını hızlı bir şekilde alabilir.)

Kod, vartamamlanan şeyin bildiriminde kullanıldığında sorun ortaya çıkar . Bu, türün açıkça belirtilmediği ve tamamlamanın çalışmayacağı anlamına gelir.

Değişken varanahtar kelimeyle bildirildiğinde kullanılan gerçek türü nasıl güvenilir bir şekilde belirleyebilirim ? Açık olmak gerekirse, çalışma zamanında belirlememe gerek yok. Bunu "Tasarım zamanında" belirlemek istiyorum.

Şimdiye kadar şu fikirlere sahibim:

  1. derleyin ve çağırın:
    • bildirim ifadesini ayıklayın, örneğin `var foo =" bir dizge değeri ";`
    • `foo.GetType () 'ifadesini birleştirmek;
    • Sonuçta ortaya çıkan C # parçasını dinamik olarak yeni bir derlemeye derleyin
    • montajı yeni bir AppDomain'e yükleyin, çerçeveyi çalıştırın ve dönüş türünü alın.
    • düzeneği boşaltın ve atın

    Tüm bunları nasıl yapacağımı biliyorum. Ancak editördeki her tamamlama isteği için kulağa çok ağır geliyor.

    Sanırım her seferinde yeni bir AppDomain'e ihtiyacım yok. Birden çok geçici montaj için tek bir AppDomain'i yeniden kullanabilir ve birden çok tamamlama talebi karşısında onu kurma ve yırtma maliyetini amorti edebilirim. Bu daha çok temel fikrin ince ayarı.

  2. IL derleyin ve inceleyin

    Bildirimi bir modülde derleyin ve ardından derleyici tarafından çıkarılan gerçek türü belirlemek için IL'yi inceleyin. Bu nasıl mümkün olabilir? Bilgi okuryazarlığını incelemek için ne kullanmalıyım?

Daha iyi bir fikir var mı? Yorumlar? öneriler?


DÜZENLEME - bunu daha fazla düşünmek, derleme ve çağırma kabul edilemez çünkü çağrının yan etkileri olabilir. Bu yüzden ilk seçenek göz ardı edilmelidir.

Ayrıca, .NET 4.0'ın varlığını varsayamayacağımı düşünüyorum.


GÜNCELLEME - Yukarıda bahsedilmeyen, ancak Eric Lippert tarafından nazikçe işaret edilen doğru cevap, tam bir aslına uygunluk türü çıkarım sistemi uygulamaktır. Tasarım zamanında bir değişkenin türünü güvenilir bir şekilde belirlemenin tek yolu budur. Ama yapmak da kolay değil. Böyle bir şeyi inşa etmeye çalıştığım yanılsamasına maruz kalmadığım için, seçenek 2'nin kısayolunu kullandım - ilgili bildirim kodunu çıkar ve derledikten sonra elde edilen IL'yi inceledim.

Bu aslında tamamlama senaryolarının adil bir alt kümesi için işe yarar.

Örneğin, aşağıdaki kod parçalarında? kullanıcının tamamlanmasını istediği konumdur. Bu çalışıyor:

var x = "hello there"; 
x.?

Tamamlanma, x'in bir Dize olduğunu anlar ve uygun seçenekleri sağlar. Bunu, aşağıdaki kaynak kodunu oluşturup sonra derleyerek yapar:

namespace N1 {
  static class dmriiann5he { // randomly-generated class name
    static void M1 () {
       var x = "hello there"; 
    }
  }
}

... ve sonra IL'yi basit bir yansımayla incelemek.

Bu aynı zamanda çalışır:

var x = new XmlDocument();
x.? 

Motor, üretilen kaynak koduna uygun kullanım maddelerini ekler, böylece düzgün bir şekilde derlenir ve ardından IL incelemesi aynı olur.

Bu da işe yarar:

var x = "hello"; 
var y = x.ToCharArray();    
var z = y.?

Bu sadece IL incelemesinin ilk yerine üçüncü yerel değişkenin türünü bulması gerektiği anlamına gelir.

Ve bu:

var foo = "Tra la la";
var fred = new System.Collections.Generic.List<String>
    {
        foo,
        foo.Length.ToString()
    };
var z = fred.Count;
var x = z.?

... önceki örnekten sadece bir seviye daha derin olan.

Ama, ne yapar değil iş başlatma bir örnek üyesi veya yerel yöntem argüman üzerinde herhangi bir noktada bağlıdır herhangi bir yerel değişkeni tamamlanmasıdır. Sevmek:

var foo = this.InstanceMethod();
foo.?

LINQ sözdizimi de.

Tamamlanması için kesinlikle "sınırlı bir tasarım" (hack için kibar bir kelime) yoluyla onları ele almayı düşünmeden önce, bu şeylerin ne kadar değerli olduğunu düşünmem gerekecek.

Yöntem bağımsız değişkenlerine veya örnek yöntemlere bağımlılıklarla sorunu ele almaya yönelik bir yaklaşım, oluşturulan, derlenen ve daha sonra IL analiz edilen kod parçasındaki bu şeylere referansları aynı türden "sentetik" yerel değişkenlerle değiştirmek olacaktır.


Başka bir Güncelleme - örnek üyelerine bağlı değişkenlerin tamamlanması artık çalışıyor.

Yaptığım şey, türü sorgulamaktı (anlambilim yoluyla) ve ardından mevcut tüm üyeler için sentetik stand-in üyeleri oluşturmaktı. Bunun gibi bir C # arabelleği için:

public class CsharpCompletion
{
    private static int PrivateStaticField1 = 17;

    string InstanceMethod1(int index)
    {
        ...lots of code here...
        return result;
    }

    public void Run(int count)
    {
        var foo = "this is a string";
        var fred = new System.Collections.Generic.List<String>
        {
            foo,
            foo.Length.ToString()
        };
        var z = fred.Count;
        var mmm = count + z + CsharpCompletion.PrivateStaticField1;
        var nnn = this.InstanceMethod1(mmm);
        var fff = nnn.?

        ...more code here...

... IL çıktısından yerel var nnn türünü öğrenebilmem için derlenen oluşturulan kod şu şekilde görünür:

namespace Nsbwhi0rdami {
  class CsharpCompletion {
    private static int PrivateStaticField1 = default(int);
    string InstanceMethod1(int index) { return default(string); }

    void M0zpstti30f4 (int count) {
       var foo = "this is a string";
       var fred = new System.Collections.Generic.List<String> { foo, foo.Length.ToString() };
       var z = fred.Count;
       var mmm = count + z + CsharpCompletion.PrivateStaticField1;
       var nnn = this.InstanceMethod1(mmm);
      }
  }
}

Tüm örnek ve statik tür üyeleri iskelet kodunda mevcuttur. Başarıyla derler. Bu noktada, yerel değişkenin türünü Yansıtma yoluyla belirlemek kolaydır.

Bunu mümkün kılan şey şudur:

  • emacs'de powershell çalıştırma yeteneği
  • C # derleyicisi gerçekten hızlıdır. Benim makinemde bir bellek içi derlemeyi derlemek yaklaşık 0,5 saniye sürüyor. Tuş vuruşları arası analiz için yeterince hızlı değil, ancak isteğe bağlı tamamlama listeleri oluşturmayı destekleyecek kadar hızlı.

Henüz LINQ'ya bakmadım.
Bu çok daha büyük bir problem olacak çünkü emacs için C # için anlamsal sözcük / ayrıştırıcı var, LINQ "yapmıyor".


4
Foo türü anlaşılır ve derleyici tarafından tür çıkarımı yoluyla doldurulur. Mekanizmaların tamamen farklı olduğundan şüpheleniyorum. Belki de tip çıkarım motorunun bir kancası vardır? En azından bir etiket olarak 'tür çıkarımı' kullanırdım.
George Mauer

3
Tüm türlere sahip ancak gerçek nesnelerin anlambilimlerine sahip olmayan "sahte" bir nesne modeli oluşturma tekniğiniz iyi bir teknik değildir. Gün içinde Visual InterDev'de JScript için IntelliSense'i bu şekilde yaptım; IE nesne modelinin tüm yöntemlere ve türlere sahip ancak yan etkilerin hiçbirine sahip olmayan "sahte" bir versiyonunu yapıyoruz ve ardından derleme zamanında ayrıştırılmış kod üzerinde küçük bir yorumlayıcı çalıştırıyoruz ve hangi türün geri geldiğini görüyoruz.
Eric Lippert

Yanıtlar:


202

Bunu "gerçek" C # IDE'de nasıl verimli bir şekilde yaptığımızı size anlatabilirim.

Yaptığımız ilk şey, yalnızca kaynak koddaki "en üst düzey" şeyleri analiz eden bir geçiş yapmaktır. Tüm yöntem gövdelerini atlıyoruz. Bu, programın kaynak kodunda hangi ad alanı, türler ve yöntemlerin (ve yapıcıların vb.) Olduğu hakkında hızlı bir şekilde bilgi veritabanı oluşturmamızı sağlar. Her yöntem gövdesindeki her bir kod satırını analiz etmek, tuş vuruşları arasında yapmaya çalışıyorsanız çok uzun sürer.

IDE'nin bir yöntem gövdesi içindeki belirli bir ifadenin türünü hesaplaması gerektiğinde - "foo" yazdığınızı söyleyin. ve foo'nun üyelerinin ne olduğunu bulmamız gerekiyor - aynı şeyi yapıyoruz; makul olarak yapabildiğimiz kadar çok işi atlıyoruz.

Yalnızca bu yöntemdeki yerel değişken bildirimlerini analiz eden bir geçişle başlıyoruz . Bu geçişi çalıştırdığımızda, bir "kapsam" ve "ad" çiftinden "tür belirleyiciye" bir eşleme yaparız. "Tür belirleyici", "Gerekirse bu yerelin türünü bulabilirim" fikrini temsil eden bir nesnedir. Bir yerel tür üzerinde çalışmak pahalı olabilir, bu yüzden gerekirse bu işi ertelemek istiyoruz.

Artık bize her yerelin türünü söyleyebilecek, tembel olarak oluşturulmuş bir veritabanımız var. Öyleyse, o "foo" ya geri dönelim. - ilgili ifadenin hangi ifadede olduğunu buluruz ve sonra anlamsal analizörü sadece bu ifadeye karşı çalıştırırız. Örneğin, yöntem gövdesine sahip olduğunuzu varsayalım:

String x = "hello";
var y = x.ToCharArray();
var z = from foo in y where foo.

ve şimdi foo'nun char türünde olduğunu bulmalıyız. Tüm meta verileri, uzantı yöntemlerini, kaynak kodu türlerini vb. İçeren bir veritabanı oluşturuyoruz. X, y ve z için tip belirleyicileri olan bir veritabanı oluşturuyoruz. İlginç ifadeyi içeren ifadeyi analiz ediyoruz. Sözdizimsel olarak dönüştürerek başlıyoruz

var z = y.Where(foo=>foo.

Foo'nun türünü bulmak için önce y'nin türünü bilmeliyiz. Öyleyse bu noktada tür belirleyiciye "y'nin türü nedir" diye soruyoruz? Daha sonra, x.ToCharArray () öğesini ayrıştıran ve "x türü nedir" diye soran bir ifade değerlendiricisi başlatır. "Mevcut bağlamda" String "i aramam gerekiyor" diyen bir tür belirleyicimiz var. Geçerli türde String türü yoktur, bu nedenle ad alanına bakarız. Orada da değil, bu yüzden using yönergelerine bakarız ve bir "using System" olduğunu ve System'ın String türüne sahip olduğunu keşfederiz. Tamam, bu x türü.

Ardından System.String'in meta verilerini ToCharArray türü için sorguluyoruz ve bunun bir System.Char [] olduğunu söylüyor. Süper. Yani y için bir tipimiz var.

Şimdi "System.Char [] 'ın bir yöntemi var mı?" Diye soruyoruz. Hayır. O halde kullanım direktiflerine bakıyoruz; Kullanılabilecek uzantı yöntemleri için tüm meta verileri içeren bir veritabanını önceden hesaplamıştık.

Şimdi "Tamam, kapsamda nerede adında on sekiz düzine genişletme yöntemi var, herhangi birinin türü System.Char [] ile uyumlu olan ilk biçimsel parametresi var mı?" Bu yüzden bir tur konvertibilite testi başlatıyoruz. Bununla birlikte, Where extension yöntemleri geneldir , bu da tür çıkarımı yapmamız gerektiği anlamına gelir.

İlk bağımsız değişkenden bir uzantı yöntemine kadar tamamlanmamış çıkarımlar yapabilen özel bir tür çıkarım motoru yazdım. Tür çıkarıcıyı çalıştırıyoruz ve bir Where yönteminin olduğunu IEnumerable<T>ve System.Char [] 'dan bir çıkarım yapabileceğimizi keşfediyoruz IEnumerable<System.Char>, yani T, System.Char.

Bu yöntemin imzası şudur Where<T>(this IEnumerable<T> items, Func<T, bool> predicate)ve T'nin System.Char olduğunu biliyoruz. Ayrıca, uzantı yönteminin parantez içindeki ilk argümanın bir lambda olduğunu biliyoruz. Bu nedenle, "foo biçimsel parametresinin System.Char olduğu varsayılır" diyen bir lambda ifade türü çıkarıcı başlatıyoruz, lambda'nın geri kalanını analiz ederken bu gerçeği kullanın.

Artık lambda gövdesini analiz etmek için ihtiyacımız olan tüm bilgilere sahibiz, bu "foo". Foo tipine bakarız, lambda bağlayıcısına göre System.Char olduğunu keşfederiz ve işimiz bitti; System.Char için tür bilgilerini görüntüleriz.

Ve tuş vuruşları arasındaki "üst düzey" analiz dışında her şeyi yapıyoruz . Bu gerçek zor kısım. Aslında tüm analizi yazmak zor değil; Yazma hızında yapabilmeniz için yeterince hızlı hale getiriyor ki bu gerçekten zor olan.

İyi şanslar!


8
Eric, tam cevap için teşekkürler. Gözlerimi epey açtın. Emacs için, kullanıcı deneyiminin kalitesi açısından Visual Studio ile rekabet edecek dinamik, tuş vuruşları arası bir motor üretmeyi hedeflemiyordum. Bir kere, ~ 0.5s doğasında gecikme nedeniyle benim tasarım, emacs tabanlı tesis ve on-demand olarak kalacaktır sadece; önden yazma önerisi yok. Bir diğeri için - var yerellerinin temel desteğini uygulayacağım, ancak işler zorlaştığında veya bağımlılık grafiği belirli bir sınırı aştığında mutlu bir şekilde oynayacağım. Bu sınırın ne olduğundan henüz emin değilim. Tekrar teşekkürler.
Cheeso

13
Dürüst olmak gerekirse, tüm bunların özellikle lambda ifadeleri ve genel tip çıkarımı ile bu kadar hızlı ve güvenilir bir şekilde çalışabilmesi aklımı karıştırıyor. Aslında ilk kez bir lambda ifadesi yazdığımda oldukça şaşırmıştım ve Intellisense'e bastığımda parametremin türünü biliyordu. Her ne kadar ifade henüz tamamlanmadı ve uzantı yöntemlerinin genel parametrelerini açıkça belirtmedim. Sihire bu küçük bakış için teşekkürler.
Dan Bryant

21
@Dan: Kaynak kodunu gördüm (veya yazdım) ve onun da işe yaraması aklımı karıştırıyor. :-) Orada bazı tüylü şeyler var.
Eric Lippert

11
Eclipse çalışanları muhtemelen bunu daha iyi yapıyor çünkü C # derleyicisinden ve IDE ekibinden daha harikalar .
Eric Lippert

23
Bu aptalca yorumu yaptığımı hiç hatırlamıyorum. Mantıklı bile değil. Sarhoş olmalıyım. Afedersiniz.
Tomas Andrle

15

Delphi IDE'nin, intellisense yapmak için Delphi derleyicisiyle nasıl çalıştığını kabaca anlatabilirim (kod içgörü, Delphi'nin dediği şeydir). C # için% 100 geçerli değildir, ancak dikkate alınması gereken ilginç bir yaklaşımdır.

Delphi'deki çoğu anlamsal analiz, ayrıştırıcının kendisinde yapılır. İfadeler, bunun kolay olmadığı durumlar haricinde, ayrıştırıldıkları gibi yazılır - bu durumda, neyin amaçlandığını belirlemek için ileriye dönük ayrıştırma kullanılır ve sonra bu karar ayrıştırmada kullanılır.

Ayrıştırma, operatör önceliği kullanılarak ayrıştırılan ifadeler dışında büyük ölçüde LL (2) özyinelemeli iniştir. Delphi ile ilgili belirgin şeylerden biri, tek geçişli bir dil olmasıdır, bu nedenle yapıların kullanılmadan önce bildirilmesi gerekir, bu nedenle bu bilgiyi ortaya çıkarmak için üst düzey geçiş gerekmez.

Bu özellik kombinasyonu, ayrıştırıcının ihtiyaç duyulan herhangi bir noktada kod içgörüsü için gereken tüm bilgilere kabaca sahip olduğu anlamına gelir. İşleyiş şekli şudur: IDE, derleyicinin sözlüğünü imlecin konumu (kod içgörüsünün istendiği nokta) hakkında bilgilendirir ve sözcü bunu özel bir simgeye dönüştürür (buna kibitz belirteci denir). Ayrıştırıcı bu belirteci karşıladığında (herhangi bir yerde olabilir), bunun sahip olduğu tüm bilgileri editöre geri gönderme sinyali olduğunu bilir. Bunu bir longjmp kullanarak yapar çünkü C ile yazılmıştır; yaptığı şey, kibitz noktasının bulunduğu sözdizimsel yapının türünü (yani gramer bağlamını) ve bu nokta için gerekli tüm sembolik tabloları nihai arayan kişiye bildirmektir. Yani mesela, bağlam, bir yöntemin argümanı olan bir ifadede ise, metot aşırı yüklerini kontrol edebilir, argüman türlerine bakabilir ve geçerli sembolleri yalnızca o argüman tipine çözümlenebilenlere filtreleyebiliriz (bu, bir açılır menüde pek çok alakasız sorun var). İç içe geçmiş bir kapsam bağlamındaysa (örneğin, "." 'Den sonra), ayrıştırıcı kapsama bir referans vermiş olacaktır ve IDE bu kapsamda bulunan tüm sembolleri numaralandırabilir.

Başka şeyler de yapılır; örneğin, yöntem gövdeleri, kibitz belirteci kendi aralıklarında yer almıyorsa atlanır - bu iyimser bir şekilde yapılır ve belirteç üzerinden atlanırsa geri alınır. Uzantı yöntemlerinin eşdeğeri - Delphi'deki sınıf yardımcıları - bir tür sürümlü önbelleğe sahiptir, bu nedenle aramaları oldukça hızlıdır. Ancak Delphi'nin genel tür çıkarımı C #'lardan çok daha zayıftır.

Şimdi, spesifik soruya: ile bildirilen değişken türlerini çıkarmak, varPascal'ın sabitlerin türünü belirleme şekline eşdeğerdir. Başlatma ifadesinin türünden gelir. Bu türler aşağıdan yukarıya doğru inşa edilmiştir. Eğer xtiptedir Integerve ytiptedir Double, daha sonra x + ytip olacaktır Doublebu dilin kuralları, çünkü; vb. Sağ tarafta tam ifade için bir türe sahip olana kadar bu kurallara uyarsınız ve bu, soldaki sembol için kullandığınız türdür.


7

Soyut sözdizimi ağacını oluşturmak için kendi ayrıştırıcınızı yazmak zorunda kalmak istemiyorsanız , her ikisi de açık kaynak olan SharpDevelop veya MonoDevelop'un ayrıştırıcılarını kullanmaya bakabilirsiniz .


4

Intellisense sistemleri tipik olarak kodu Özet Sözdizimi Ağacı kullanarak temsil eder, bu onların 'var' değişkenine atanan fonksiyonun dönüş türünü derleyicinin yapacağı gibi aşağı yukarı aynı şekilde çözmelerine olanak tanır. VS Intellisense kullanırsanız, geçerli (çözülebilir) bir atama ifadesi girmeyi tamamlayana kadar size değişken türünü vermeyeceğini fark edebilirsiniz. İfade hala belirsizse (örneğin, ifade için genel bağımsız değişkenleri tam olarak çıkaramazsa), var türü çözümlenmez. Türü çözmek için bir ağacın oldukça derinlerine inmeniz gerekebileceğinden, bu oldukça karmaşık bir süreç olabilir. Örneğin:

var items = myList.OfType<Foo>().Select(foo => foo.Bar);

Dönüş türü IEnumerable<Bar>, ancak bunu çözmek için şunları bilmek gerekir:

  1. myList, uygulayan türdendir IEnumerable.
  2. OfType<T>IEnumerable için geçerli olan bir uzantı yöntemi vardır.
  3. Ortaya çıkan değer IEnumerable<Foo>ve buna Selectuygulanan bir genişletme yöntemi vardır.
  4. Lambda ifadesi foo => foo.Bar, Foo türünde foo parametresine sahiptir. Bu, a alan Seçin kullanımıyla çıkarılır Func<TIn,TOut>ve TIn bilindiği için (Foo), foo türü çıkarılabilir.
  5. Foo türü, Bar türünde bir Bar özelliğine sahiptir. Select dönüşlerinin IEnumerable<TOut>ve TOut'un lambda ifadesinin sonucundan çıkarılabileceğini biliyoruz , bu nedenle ortaya çıkan öğe türü olmalıdır IEnumerable<Bar>.

Doğru, oldukça derinleşebilir. Tüm bağımlılıkları çözmekte rahatım. Sadece bunu düşünüyorum, anlattığım ilk seçenek - derle ve çağır - kesinlikle kabul edilemez, çünkü kod çağırmanın bir veritabanını güncellemek gibi yan etkileri olabilir ve bu bir editörün yapması gereken bir şey değildir. Derleme tamam, çağırmak değil. AST'yi oluşturma konusunda, bunu yapmak istediğimi sanmıyorum. Gerçekten bu işi, nasıl yapılacağını zaten bilen derleyiciye ertelemek istiyorum. Derleyiciden bilmek istediklerimi bana söylemesini isteyebilmek istiyorum. Sadece basit bir cevap istiyorum.
Cheeso

Derlemeden incelemenin zorluğu, bağımlılıkların keyfi olarak derin olabilmesidir; bu, derleyicinin kod üretmesi için her şeyi oluşturmanız gerekebileceği anlamına gelir. Bunu yaparsanız, oluşturulan IL ile hata ayıklayıcı sembollerini kullanabileceğinizi ve her yerelin türünü sembolüyle eşleştirebileceğinizi düşünüyorum.
Dan Bryant

1
@Cheeso: Derleyici bu tür bir tür analizi hizmet olarak sunmuyor. Umarım gelecekte olur ama söz vermez.
Eric Lippert

evet, bence gitmenin yolu bu olabilir - tüm bağımlılıkları çözün ve ardından bilgi okuryazarlığını derleyin ve inceleyin. @Eric, bilmek güzel. Şimdilik, tam AST analizini yapmak istemiyorsam, bu hizmeti mevcut araçları kullanarak üretmek için kirli bir hack'e başvurmalıyım. Örneğin, akıllıca oluşturulmuş bir kod parçası derleyin ve aradığım yanıtı almak için programlı olarak ILDASM (veya benzerini) kullanın.
Cheeso

4

Emacs'ı hedeflediğiniz için, CEDET paketiyle başlamak en iyisi olabilir. Eric Lippert'in tüm ayrıntıları CEDET / Anlamsal C ++ aracındaki kod çözümleyicide zaten ele alınmıştır. Ayrıca bir C # ayrıştırıcısı da vardır (muhtemelen biraz TLC gerektirir), bu nedenle eksik olan tek parça C # için gerekli parçaların ayarlanmasıyla ilgilidir.

Temel davranışlar, dil bazında tanımlanan aşırı yüklenebilir işlevlere bağlı olan çekirdek algoritmalarda tanımlanır. Tamamlama motorunun başarısı, ne kadar ayar yapıldığına bağlıdır. Kılavuz olarak c ++ ile, C ++ 'ya benzer bir destek almak çok kötü olmamalıdır.

Daniel'in cevabı, ayrıştırma ve analiz yapmak için MonoDevelop'u kullanmayı önerir. Bu, mevcut C # ayrıştırıcı yerine alternatif bir mekanizma olabilir veya mevcut ayrıştırıcıyı büyütmek için kullanılabilir.


Doğru, CEDET'i biliyorum ve anlamsallık için Contrib dizinindeki C # desteğini kullanıyorum. Anlamsal, yerel değişkenlerin ve türlerinin listesini sağlar. Bir tamamlama motoru bu listeyi tarayabilir ve kullanıcıya doğru seçenekleri sunabilir. Sorun, değişkenin olduğu zamandır var. Anlamsal, onu var olarak doğru bir şekilde tanımlar, ancak tür çıkarımı sağlamaz. Sorum özellikle etrafında nasıl ele oldu o . Ayrıca mevcut CEDET tamamlamasına bağlanmaya da baktım, ancak nasıl olduğunu bulamadım. CEDET için dokümantasyon ... ah ... tam değil.
Cheeso

Yan yorum - CEDET hayranlık uyandıracak kadar hırslı, ancak kullanması ve genişletmesi zor geldi. Şu anda ayrıştırıcı, "ad alanını" C # 'da bir sınıf göstergesi olarak ele almaktadır . "İsim alanını" ayrı bir sözdizimsel öğe olarak nasıl ekleyeceğimi bile çözemedim. Bunu yapmak diğer tüm sözdizimsel analizleri engelledi ve nedenini anlayamadım. Daha önce tamamlama çerçevesi ile yaşadığım zorluğu açıklamıştım. Bu sorunların ötesinde, parçalar arasında dikişler ve örtüşmeler vardır. Bir örnek olarak, navigasyon hem semantik hem de senatörün bir parçasıdır. CEDET baştan çıkarıcı görünüyor, ama sonuçta ... bağlanmak için çok hantal.
Cheeso

Cheeso, CEDET'in daha az belgelenmiş bölümlerinden en iyi şekilde yararlanmak istiyorsanız, yapabileceğiniz en iyi şey posta listesini denemektir. Sorular için henüz iyi geliştirilmemiş alanlara dalmak kolaydır, bu nedenle iyi çözümler bulmak veya mevcut olanları açıklamak için birkaç yineleme gerekir. Özellikle C # için, onun hakkında hiçbir şey bilmediğim için, tek seferlik basit cevaplar olmayacak.
Eric

2

İyi yapmak zor bir problem. Temel olarak, dil belirleme / derleyiciyi lexing / ayrıştırma / yazım denetimi yoluyla modellemeniz ve daha sonra sorgulayabileceğiniz kaynak kodun dahili bir modelini oluşturmanız gerekir. Eric, C # için ayrıntılı olarak açıklıyor. Her zaman F # derleyici kaynak kodunu (F # CTP'nin bir parçası) indirebilir ve F # service.fsidil hizmetinin akıllıca içerik sağlamak için kullandığı F # derleyicisinden açığa çıkan arayüzü, çıkarsanan türler için araç ipuçları vb. Görmek için bir göz atabilirsiniz . Derleyiciyi arayabileceğiniz bir API olarak zaten mevcutsa, olası bir 'arayüz' hissi.

Diğer yol, derleyicileri açıkladığınız gibi yeniden kullanmak ve ardından yansıtma kullanmak veya üretilen koda bakmaktır. Bu, bir derleyiciden bir derleme çıktısı almak için 'tam programlara' ihtiyaç duymanız açısından sorunludur, oysa düzenleyicide kaynak kodunu düzenlerken, genellikle henüz ayrıştırmayan 'kısmi programlara' sahip olursunuz. henüz tüm yöntemler uygulandı, vb.

Kısacası, 'düşük bütçeli' versiyonun iyi yapılmasının çok zor olduğunu ve 'gerçek' versiyonun iyi yapılması çok, çok zor olduğunu düşünüyorum. (Burada 'zor', hem 'çabayı' hem de 'teknik zorluğu' ölçer.)


Ya, "düşük bütçeli" versiyonun bazı açık sınırlamaları vardır. "Yeterince iyi" nin ne olduğuna ve o bara ulaşıp ulaşamayacağıma karar vermeye çalışıyorum. Şimdiye kadar sahip olduğum deneyimleri test etme deneyimime göre, emacs içinde C # yazmayı çok daha güzel hale getiriyor.
Cheeso


0

Çözüm "1" için .NET 4'te bunu hızlı ve kolay bir şekilde yapacak yeni bir tesisiniz var. Bu yüzden, programınızı .NET 4'e çevirebiliyorsanız, bu sizin en iyi seçiminiz olacaktır.

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.