ฉันกำลังดำเนินการสิ่งอำนวยความสะดวก (Intellisense) สำหรับ C # ใน emacs
แนวคิดคือถ้าผู้ใช้พิมพ์แฟรกเมนต์แล้วขอให้ดำเนินการเสร็จสิ้นโดยใช้การกดแป้นพิมพ์ร่วมกันสิ่งอำนวยความสะดวกในการทำให้เสร็จสมบูรณ์จะใช้การสะท้อน. NET เพื่อพิจารณาความสำเร็จที่เป็นไปได้
การทำเช่นนี้ต้องการให้ทราบประเภทของสิ่งที่จะเสร็จสมบูรณ์ หากเป็นสตริงจะมีชุดวิธีการและคุณสมบัติที่เป็นไปได้ ถ้าเป็น Int32 จะมีชุดแยกต่างหากและอื่น ๆ
การใช้ความหมายแพคเกจรหัส lexer / parser ที่มีอยู่ใน emacs ฉันสามารถค้นหาการประกาศตัวแปรและประเภทของตัวแปรได้ ด้วยเหตุนี้จึงเป็นเรื่องง่ายที่จะใช้การสะท้อนเพื่อรับวิธีการและคุณสมบัติของประเภทจากนั้นจึงนำเสนอรายการตัวเลือกให้กับผู้ใช้ (โอเคไม่ตรงไปตรงมาที่จะทำภายใน emacs แต่การใช้ความสามารถในการเรียกใช้กระบวนการ powershell ภายใน emacsมันจะง่ายกว่ามากฉันเขียนแอสเซมบลี. NET ที่กำหนดเองเพื่อทำการสะท้อนโหลดลงใน powershell จากนั้น elisp ทำงานภายใน emacs สามารถส่งคำสั่งไปยัง powershell และอ่านการตอบกลับผ่าน comint ดังนั้น emacs จึงได้ผลลัพธ์ของการสะท้อนกลับอย่างรวดเร็ว)
ปัญหามาถึงเมื่อรหัสใช้var
ในการประกาศสิ่งที่เสร็จสมบูรณ์ นั่นหมายความว่าไม่มีการระบุประเภทอย่างชัดเจนและการกรอกข้อมูลจะไม่ทำงาน
ฉันจะกำหนดประเภทจริงที่ใช้อย่างน่าเชื่อถือได้อย่างไรเมื่อประกาศตัวแปรด้วยvar
คีย์เวิร์ด เพื่อความชัดเจนฉันไม่จำเป็นต้องกำหนดตอนรันไทม์ ฉันต้องการตรวจสอบที่ "เวลาออกแบบ"
จนถึงตอนนี้ฉันมีแนวคิดเหล่านี้:
- รวบรวมและเรียกใช้:
- แยกคำสั่งประกาศเช่น `var foo =" a string value ";"
- เชื่อมต่อคำสั่ง `foo.GetType ();"
- รวบรวมชิ้นส่วน C # ที่เป็นผลลัพธ์แบบไดนามิกลงในแอสเซมบลีใหม่
- โหลดแอสเซมบลีลงใน AppDomain ใหม่เรียกใช้ framgment และรับประเภทการส่งคืน
- ยกเลิกการโหลดและทิ้งชุดประกอบ
ฉันรู้วิธีทำทั้งหมดนี้ แต่ฟังดูมีน้ำหนักมากสำหรับการร้องขอการกรอกข้อมูลแต่ละครั้งในโปรแกรมแก้ไข
ฉันคิดว่าฉันไม่ต้องการ AppDomain ใหม่ทุกครั้ง ฉันสามารถใช้ AppDomain เดียวซ้ำสำหรับแอสเซมบลีชั่วคราวหลาย ๆ แอสเซมบลีและตัดทอนค่าใช้จ่ายในการตั้งค่าและฉีกมันลงในคำขอที่ทำให้เสร็จสมบูรณ์หลายรายการ นั่นเป็นการปรับเปลี่ยนแนวคิดพื้นฐานมากกว่า
- รวบรวมและตรวจสอบ IL
เพียงรวบรวมคำประกาศลงในโมดูลจากนั้นตรวจสอบ IL เพื่อกำหนดประเภทจริงที่คอมไพเลอร์อนุมาน จะเป็นไปได้อย่างไร? ฉันจะใช้อะไรในการตรวจสอบ IL
มีความคิดที่ดีกว่านี้ไหม ความคิดเห็น? ข้อเสนอแนะ?
แก้ไข - การคิดถึงสิ่งนี้ต่อไปการคอมไพล์และเรียกใช้ไม่เป็นที่ยอมรับเนื่องจากการเรียกใช้อาจมีผลข้างเคียง ดังนั้นตัวเลือกแรกจะต้องถูกตัดออก
นอกจากนี้ฉันคิดว่าฉันไม่สามารถสันนิษฐานได้ว่ามี. NET 4.0
อัปเดต - คำตอบที่ถูกต้องซึ่งไม่ได้กล่าวถึงข้างต้น แต่ Eric Lippert ชี้ให้เห็นอย่างนุ่มนวลคือการใช้ระบบการอนุมานประเภทความเที่ยงตรงเต็มรูปแบบ เป็นวิธีเดียวที่จะกำหนดประเภทของตัวแปรในเวลาออกแบบได้อย่างน่าเชื่อถือ แต่ก็ไม่ใช่เรื่องง่ายที่จะทำ เนื่องจากฉันไม่ต้องทนทุกข์ทรมานจากภาพลวงตาที่ฉันต้องการพยายามสร้างสิ่งนี้ฉันจึงใช้ทางลัดของตัวเลือกที่ 2 - แยกรหัสการประกาศที่เกี่ยวข้องและรวบรวมจากนั้นตรวจสอบ IL ที่เป็นผลลัพธ์
สิ่งนี้ใช้งานได้จริงสำหรับส่วนย่อยที่เป็นธรรมของสถานการณ์ที่เสร็จสมบูรณ์
ตัวอย่างเช่นสมมติว่าในส่วนของรหัสต่อไปนี้? คือตำแหน่งที่ผู้ใช้ขอให้เสร็จสิ้น ใช้งานได้:
var x = "hello there";
x.?
การทำให้สมบูรณ์ตระหนักว่า x เป็นสตริงและมีตัวเลือกที่เหมาะสม ทำได้โดยการสร้างและรวบรวมซอร์สโค้ดต่อไปนี้:
namespace N1 {
static class dmriiann5he { // randomly-generated class name
static void M1 () {
var x = "hello there";
}
}
}
... จากนั้นตรวจสอบ IL ด้วยการสะท้อนแสงอย่างง่าย
สิ่งนี้ยังใช้งานได้:
var x = new XmlDocument();
x.?
เอ็นจิ้นจะเพิ่มส่วนคำสั่งโดยใช้ที่เหมาะสมให้กับซอร์สโค้ดที่สร้างขึ้นเพื่อให้คอมไพล์ถูกต้องจากนั้นการตรวจสอบ IL จะเหมือนกัน
ใช้งานได้เช่นกัน:
var x = "hello";
var y = x.ToCharArray();
var z = y.?
นั่นหมายความว่าการตรวจสอบ IL ต้องหาประเภทของตัวแปรท้องถิ่นตัวที่สามแทนที่จะเป็นตัวแรก
และนี่:
var foo = "Tra la la";
var fred = new System.Collections.Generic.List<String>
{
foo,
foo.Length.ToString()
};
var z = fred.Count;
var x = z.?
... ซึ่งเป็นเพียงระดับหนึ่งที่ลึกกว่าตัวอย่างก่อนหน้านี้
แต่สิ่งที่ใช้ไม่ได้คือการทำให้ตัวแปรโลคัลเสร็จสิ้นซึ่งการเริ่มต้นขึ้นอยู่กับจุดใด ๆ บนสมาชิกอินสแตนซ์หรืออาร์กิวเมนต์เมธอดเฉพาะที่ ชอบ:
var foo = this.InstanceMethod();
foo.?
หรือไวยากรณ์ LINQ
ฉันจะต้องคิดว่าสิ่งเหล่านั้นมีค่าเพียงใดก่อนที่ฉันจะพิจารณาจัดการกับสิ่งที่แน่นอนคือ "การออกแบบที่ จำกัด " (คำที่สุภาพสำหรับการแฮ็ก) เพื่อให้เสร็จสมบูรณ์
แนวทางในการแก้ไขปัญหาด้วยการพึ่งพาอาร์กิวเมนต์ของวิธีการหรือวิธีการอินสแตนซ์จะถูกแทนที่ในส่วนของโค้ดที่สร้างรวบรวมและวิเคราะห์จาก IL การอ้างอิงถึงสิ่งเหล่านั้นด้วยตัวแปรท้องถิ่น "สังเคราะห์" ที่เป็นประเภทเดียวกัน
การอัปเดตอื่น - การเสร็จสิ้นบน vars ที่ขึ้นอยู่กับสมาชิกอินสแตนซ์ตอนนี้ใช้งานได้
สิ่งที่ฉันทำคือซักถามประเภท (ผ่านทางความหมาย) จากนั้นสร้างสมาชิกสแตนด์อินสังเคราะห์สำหรับสมาชิกที่มีอยู่ทั้งหมด สำหรับบัฟเฟอร์ C # เช่นนี้:
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 ประเภทของ var nnn ในเครื่องมีลักษณะดังนี้:
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);
}
}
}
สมาชิกอินสแตนซ์และประเภทคงที่ทั้งหมดมีอยู่ในรหัสโครงกระดูก มันรวบรวมสำเร็จ ณ จุดนั้นการกำหนดประเภทของตัวแปรท้องถิ่นนั้นตรงไปตรงมาผ่านการสะท้อนกลับ
สิ่งที่ทำให้เป็นไปได้คือ:
- ความสามารถในการเรียกใช้ powershell ใน emacs
- คอมไพเลอร์ C # นั้นเร็วมาก ในเครื่องของฉันใช้เวลาประมาณ 0.5 วินาทีในการรวบรวมชุดประกอบในหน่วยความจำ ไม่เร็วพอสำหรับการวิเคราะห์ระหว่างการกดแป้นพิมพ์ แต่เร็วพอที่จะรองรับการสร้างรายการที่เสร็จสมบูรณ์ตามความต้องการ
ฉันยังไม่ได้ดู LINQ
นั่นจะเป็นปัญหาที่ใหญ่กว่ามากเนื่องจาก emacs / parser ความหมายมีไว้สำหรับ C # ไม่ "ทำ" LINQ