需要将以下代码从Ruby转换为C#.但是我对使用yield关键字和Ruby的一般语法感到困惑.任何知道一点点Ruby的人都可以帮忙并转换代码
class < < Cache
STALE_REFRESH = 1
STALE_CREATED = 2
# Caches data received from a block
#
# The difference between this method and usual Cache.get
# is following: this method caches data and allows user
# to re-generate data when it is expired w/o running
# data generation code more than once so dog-pile effect
# won't bring our servers down
#
def smart_get(key, ttl = nil, generation_time = 30.seconds)
# Fallback to default caching approach if no ttl given
return get(key) { yield } unless ttl
# Create window for data refresh
real_ttl = ttl + generation_time * 2
stale_key = "#{key}.stale"
# Try to get data from memcache
value = get(key)
stale = get(stale_key)
# If stale key has expired, it is time to re-generate our data
unless stale
put(stale_key, STALE_REFRESH, generation_time) # lock
value = nil # force data re-generation
end
# If no data retrieved or data re-generation forced, re-generate data and reset stale key
unless value
value = yield
put(key, value, real_ttl)
put(stale_key, STALE_CREATED, ttl) # unlock
end
return value
end
Run Code Online (Sandbox Code Playgroud)
结束
Jör*_*tag 12
我根本不知道C#,所以我对C#说的任何话都应该带着一点点盐.但是,我将尝试解释那段Ruby代码中发生的事情.
class << Cache
Run Code Online (Sandbox Code Playgroud)
Ruby有一种称为单例方法的东西.这些与Singleton软件设计模式无关,它们只是为一个且只有一个对象定义的方法.因此,您可以拥有同一个类的两个实例,并将方法添加到这两个对象之一.
单例方法有两种不同的语法.一种是在方法的名称前加上对象的前缀,因此只为对象def foo.bar(baz)定义一个方法.另一种方法称为打开单例类,它在语法上看起来类似于定义一个类,因为这也是语义上发生的事情:单例方法实际上存在于一个不可见的类中,该类插入到对象与类层次结构中的实际类之间.barfoo
这个语法如下所示:class << foo.这将打开单例类对象,foo并且在该类主体内定义的每个方法都成为对象的单例方法foo.
为什么在这里使用?好吧,Ruby是一种纯粹的面向对象语言,这意味着包括类在内的所有东西都是一个对象.现在,如果可以将方法添加到单个对象,并且类是对象,则这意味着可以将方法添加到单个类中.换句话说,Ruby不需要常规方法和静态方法之间的人为区分(无论如何它们都是欺诈:它们不是真正的方法,只是美化程序).什么是C#中的静态方法,它只是类对象的单例类的常规方法.
所有这一切都解释之间界定的一切只是一个冗长的方式class << Cache及其相应end变static.
STALE_REFRESH = 1
STALE_CREATED = 2
Run Code Online (Sandbox Code Playgroud)
在Ruby中,每个以大写字母开头的变量实际上都是常量.但是,在这种情况下,我们不会将它们翻译为static const字段,而是将其翻译为enum,因为这是它们的使用方式.
# Caches data received from a block
#
# The difference between this method and usual Cache.get
# is following: this method caches data and allows user
# to re-generate data when it is expired w/o running
# data generation code more than once so dog-pile effect
# won't bring our servers down
#
def smart_get(key, ttl = nil, generation_time = 30.seconds)
Run Code Online (Sandbox Code Playgroud)
这个方法有三个参数(实际上有四个,我们将在后面看到确切的原因),其中两个是可选的(ttl和generation_time).它们都有一个默认值,但是,如果ttl没有真正使用默认值,它更多地作为标记来查明参数是否被传入.
30.seconds是ActiveSupport库添加到Integer类中的扩展.它实际上没有做任何事情,它只是返回self.在这种情况下使用它只是为了使方法定义更具可读性.(还有其它方法做一些更有益的,例如Integer#minutes,返回self * 60和Integer#hours等).我们将以此作为参考,该参数的类型不应该int,而是System.TimeSpan.
# Fallback to default caching approach if no ttl given
return get(key) { yield } unless ttl
Run Code Online (Sandbox Code Playgroud)
这包含几个复杂的Ruby构造.让我们从最简单的一个开始:尾随条件修饰符.如果条件体仅包含一个表达式,则条件可以附加到表达式的末尾.所以,而不是说if a > b then foo end你也可以说foo if a > b.所以,上面相当于unless ttl then return get(key) { yield } end.
下一个也很容易:unless只是语法糖if not.所以,我们现在在if not ttl then return get(key) { yield } end
第三是Ruby的真相系统.在Ruby中,事实非常简单.实际上,虚假很简单,而且事实很明显:特殊关键字false是假的,特殊关键字nil是假的,其他一切都是真的.所以,在这种情况下,条件只会是真的,如果ttl是false或者nil.false对于时间跨度来说,这不是一个可怕的合理价值,所以唯一有趣的是nil.该片段的写法更加清晰:if ttl.nil? then return get(key) { yield } end.由于ttl参数的默认值为nil,如果没有传入参数,则此条件为true ttl.因此,条件用于计算方法被调用了多少个参数,这意味着我们不会将其转换为条件而是作为方法重载.
现在,到了yield.在Ruby中,每个方法都可以接受隐式代码块作为参数.这就是为什么我在上面写道,该方法实际上需要四个参数,而不是三个.代码块只是一段匿名代码,可以传递,存储在变量中,以后再调用.Ruby继承了Smalltalk的块,但这个概念可以追溯到1958年,直到Lisp的lambda表达式.在提到匿名代码块时,至少现在,在提到lambda表达式时,您应该知道如何表示这个隐式的第四个方法参数:委托类型,更具体地说,a Func.
那么,有什么用yield?它将控制转移到块.它基本上只是一种调用块的非常方便的方式,而不必将其显式存储在变量中然后调用它.
# Create window for data refresh
real_ttl = ttl + generation_time * 2
stale_key = "#{key}.stale"
Run Code Online (Sandbox Code Playgroud)
此#{foo}语法称为字符串插值.它意味着"用任何评估大括号之间表达式的结果替换字符串中的标记".它只是一个非常简洁的版本String.Format(),这正是我们要将其翻译成的.
# Try to get data from memcache
value = get(key)
stale = get(stale_key)
# If stale key has expired, it is time to re-generate our data
unless stale
put(stale_key, STALE_REFRESH, generation_time) # lock
value = nil # force data re-generation
end
# If no data retrieved or data re-generation forced, re-generate data and reset stale key
unless value
value = yield
put(key, value, real_ttl)
put(stale_key, STALE_CREATED, ttl) # unlock
end
return value
end
end
Run Code Online (Sandbox Code Playgroud)
这是我将Ruby版本转换为C#的微弱尝试:
public class Cache<Tkey, Tvalue> {
enum Stale { Refresh, Created }
/* Caches data received from a delegate
*
* The difference between this method and usual Cache.get
* is following: this method caches data and allows user
* to re-generate data when it is expired w/o running
* data generation code more than once so dog-pile effect
* won't bring our servers down
*/
public static Tvalue SmartGet(Tkey key, TimeSpan ttl, TimeSpan generationTime, Func<Tvalue> strategy)
{
// Create window for data refresh
var realTtl = ttl + generationTime * 2;
var staleKey = String.Format("{0}stale", key);
// Try to get data from memcache
var value = Get(key);
var stale = Get(staleKey);
// If stale key has expired, it is time to re-generate our data
if (stale == null)
{
Put(staleKey, Stale.Refresh, generationTime); // lock
value = null; // force data re-generation
}
// If no data retrieved or data re-generation forced, re-generate data and reset stale key
if (value == null)
{
value = strategy();
Put(key, value, realTtl);
Put(staleKey, Stale.Created, ttl) // unlock
}
return value;
}
// Fallback to default caching approach if no ttl given
public static Tvalue SmartGet(Tkey key, Func<Tvalue> strategy) =>
Get(key, strategy);
// Simulate default argument for generationTime
// C# 4.0 has default arguments, so this wouldn't be needed.
public static Tvalue SmartGet(Tkey key, TimeSpan ttl, Func<Tvalue> strategy) =>
SmartGet(key, ttl, new TimeSpan(0, 0, 30), strategy);
// Convenience overloads to allow calling it the same way as
// in Ruby, by just passing in the timespans as integers in
// seconds.
public static Tvalue SmartGet(Tkey key, int ttl, int generationTime, Func<Tvalue> strategy) =>
SmartGet(key, new TimeSpan(0, 0, ttl), new TimeSpan(0, 0, generationTime), strategy);
public static Tvalue SmartGet(Tkey key, int ttl, Func<Tvalue> strategy) =>
SmartGet(key, new TimeSpan(0, 0, ttl), strategy);
}
Run Code Online (Sandbox Code Playgroud)
请注意,我不知道C#,我不知道.NET,我没有测试过这个,我甚至不知道它是否在语法上有效.无论如何希望它有所帮助.
如果缓存不包含所请求的数据(yield如何调用块),则此代码将被传递给要评估的块.这是相当惯用的红宝石代码; 我不知道你是怎么(或者甚至)将它"翻译"成c#.
寻找一个用例来看我的意思.你应该找到像这样模糊的东西:
x = smart_get([:foo,"bar"]) { call_expensive_operation_foo("bar") }
Run Code Online (Sandbox Code Playgroud)
更好的选择是弄清楚你需要它做什么,然后写一些在c#中重新创建的东西,而不是试图从ruby中"翻译".
| 归档时间: |
|
| 查看次数: |
15078 次 |
| 最近记录: |