2009-10-04 23 views
10

Tôi đã đọc qua một số bài viết về Caching and Memoization và cách thực hiện dễ dàng bằng cách sử dụng các đại biểu và Generics. Cú pháp này khá đơn giản và dễ dàng thực hiện, nhưng tôi chỉ cảm thấy do tính chất lặp đi lặp lại nên có thể tạo mã dựa trên một thuộc tính, thay vì phải viết cùng một mã hệ thống ống nước.Làm cách nào để tiêm/tạo mã ống nước vào các phương pháp được trang trí với Thuộc tính?

Hãy nói rằng chúng tôi bắt đầu với ví dụ mặc định:

class Foo 
{ 
    public int Fibonacci(int n) 
    { 
    return n > 1 ? Fibonacci(n-1) + Fibonacci(n-2) : n; 
    } 
} 

Và sau đó đến memoize này:

// Let's say we have a utility class somewhere with the following extension method: 
// public static Func<TResult> Memoize<TResult>(this Func<TResult> f) 

class Foo 
{ 
    public Func<int,int> Fibonacci = fib; 

    public Foo() 
    { 
    Fibonacci = Fibonacci.Memoize(); 
    } 

    public int fib(int n) 
    { 
    return n > 1 ? Fibonacci(n-1) + Fibonacci(n-2) : n; 
    } 
} 

tôi nghĩ, nó sẽ không thể đơn giản hơn chỉ cần thực hiện một bộ tạo mã để spits ra mã này, một khi nó tìm thấy một phương pháp được gắn thẻ phù hợp với một trong các phương pháp mở rộng Memoize. Vì vậy, thay vì phải viết mã hệ thống ống nước này, tôi chỉ có thể thêm một thuộc tính:

class Foo 
{ 
    [Memoize] 
    public int Fibonacci(int n) 
    { 
    return n > 1 ? Fibonacci(n-1) + Fibonacci(n-2) : n; 
    } 
} 

Thành thực mà nói, tôi biết điều này là nhìn giống như đường biên dịch cần được chuyển đổi bởi một tiền xử lý so với thế hệ mã thực tế nhưng câu hỏi của tôi là:

  1. bạn nghĩ gì là cách tốt nhất để tìm các phương pháp trong file aC# nguồn mà có một thuộc tính nhất định, phân tích ra parametertypes và returntype, và tạo ra một đại biểu phù hợp với dấu vân tay này
  2. Điều gì sẽ là cách tốt nhất để tích hợp điều này vào quá trình xây dựng, mà không thực sự qua viết mã của tôi. Có thể làm một số tiền xử lý trên các tập tin nguồn trước khi chuyển nó vào trình biên dịch?

Cảm ơn bất kỳ và tất cả ý tưởng.

Cập nhật:

tôi đã nhìn vào thư viện PostSharp Shay như đã gợi ý, và nó có vẻ rất phù hợp cho công việc trên các ứng dụng phi thời gian quan trọng như quản lý giao dịch, Tracing hoặc Security.

Tuy nhiên, khi sử dụng nó trong một bối cảnh thời gian quan trọng, nó đã chứng minh là chậm hơn rất nhiều so với đại biểu. Một triệu lần lặp của ví dụ Fibonacci với mỗi lần thực hiện dẫn đến thời gian chạy chậm hơn 80 lần. (0,012ms postsharp và 0,00015ms đại biểu cho mỗi cuộc gọi)

Nhưng thành thật mà nói, kết quả là hoàn toàn có thể chấp nhận được trong ngữ cảnh mà tôi định sử dụng nó. Cảm ơn bạn đã trả lời!

Update2:

Rõ ràng tác giả của PostSharp đang làm việc chăm chỉ trên release 2.0 mà sẽ bao gồm, trong số những thứ khác, cải tiến hiệu suất trong mã sản xuất, và thời gian biên dịch.

Trả lời

6
+0

Điều này trông giống như một lựa chọn rất tốt. Hãy nhìn vào nó. –

+0

Tôi đã bỏ phiếu này, nhưng tôi có thể hơi thiên vị :). –

+0

@Patrick, vì vậy bạn làm tất cả công việc khó khăn và tôi nhận được phiếu bầu :) –

3

Tôi đã sử dụng chức năng Memoize sau đây trong một dự án của tôi:

public class Foo 
{ 
    public int Fibonacci(int n) 
    { 
     return n > 1 ? Fibonacci(n - 1) + Fibonacci(n - 2) : n; 
    } 
} 

class Program 
{ 
    public static Func<Т, TResult> Memoize<Т, TResult>(Func<Т, TResult> f) where Т : IEquatable<Т> 
    { 
     Dictionary<Т, TResult> map = new Dictionary<Т, TResult>(); 
     return a => 
     { 
      TResult local; 
      if (!TryGetValue<Т, TResult>(map, a, out local)) 
      { 
       local = f(a); 
       map.Add(a, local); 
      } 
      return local; 
     }; 
    } 

    private static bool TryGetValue<Т, TResult>(Dictionary<Т, TResult> map, Т key, out TResult value) where Т : IEquatable<Т> 
    { 
     EqualityComparer<Т> comparer = EqualityComparer<Т>.Default; 
     foreach (KeyValuePair<Т, TResult> pair in map) 
     { 
      if (comparer.Equals(pair.Key, key)) 
      { 
       value = pair.Value; 
       return true; 
      } 
     } 
     value = default(TResult); 
     return false; 
    } 


    static void Main(string[] args) 
    { 
     var foo = new Foo(); 
     // Transform the original function and render it with memory 
     var memoizedFibonacci = Memoize<int, int>(foo.Fibonacci); 

     // memoizedFibonacci is a transformation of the original function that can be used from now on: 
     // Note that only the first call will hit the original function 
     Console.WriteLine(memoizedFibonacci(3)); 
     Console.WriteLine(memoizedFibonacci(3)); 
     Console.WriteLine(memoizedFibonacci(3)); 
     Console.WriteLine(memoizedFibonacci(3)); 
    } 
} 

Trong dự án của tôi, tôi chỉ cần thêm chức năng với một đối số duy nhất mà thực hiện IEquatable<Т> nhưng điều này có thể được khái quát hóa hơn nữa. Một nhận xét quan trọng khác là mã này không phải là chủ đề an toàn.Nếu bạn cần an toàn luồng, bạn sẽ cần phải đồng bộ hóa quyền truy cập đọc/ghi vào bản đồ nội bộ có thể bắt đầu.

+0

Đây không phải là câu hỏi của tôi. Kiểm tra các dấu đầu dòng ở dưới cùng. –

1

Để giải quyết đặc biệt điểm của bạn:

  1. này có thể sẽ quá khó để làm theo cách mà bạn mô tả, như bạn cần một full- thổi C# ngữ pháp trình phân tích cú pháp. Cách thay thế khả thi hơn là viết một ứng dụng được quản lý có thể tải thông tin được biên dịch và trích xuất thông tin bằng cách sử dụng Phản chiếu. Điều này sẽ liên quan đến việc nhận tất cả các đối tượng Loại trong một hội đồng nhất định, tìm kiếm cho các phương pháp trên các loại, truy xuất thuộc tính tùy chỉnh, và sau đó phát ra mã ghi nhớ (phần này có thể hơi khó).
  2. Nếu bạn đi tuyến đường tôi đề cập trong # 1, bạn có thể chỉ cần thêm bước sau khi xây dựng để chạy công cụ của mình. Visual Studio (sử dụng MSBuild bên dưới) làm cho điều này tương đối dễ dàng.
1

Nếu bạn viết một plugin cho PostSharp thay vì sử dụng thư viện LAOS, bạn sẽ không bị ảnh hưởng.

+0

Thú vị, cảm ơn! –

Các vấn đề liên quan