将Ruby转换为C#
需要将以下代码从Ruby转换为C#。然而,我对使用yield关键字和Ruby的一般语法感到困惑。任何懂一点Ruby的人都可以帮忙转换代码吗将Ruby转换为C#,c#,ruby,caching,C#,Ruby,Caching,需要将以下代码从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 metho
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
class<
结束如果缓存中不包含请求的数据,则此代码似乎正在传递一个要评估的块(
yield
是对该块的调用方式)。这是相当地道的ruby代码;我不知道你如何(甚至是否)把它“翻译”成c
寻找一个用例来了解我的意思。你会发现一些模糊的东西,比如:
x = smart_get([:foo,"bar"]) { call_expensive_operation_foo("bar") }
更好的办法是找出需要它做什么,并用c#编写一些从头开始的东西,而不是尝试从ruby“翻译”。看起来您正在尝试将memcache客户端从ruby移植到c#。如果是这样,使用本机C#memcache客户端实现可能更容易,例如: 不管是哪种方式,我大体上同意MarkusQ的观点,即从高级语言到低级语言的翻译,总的来说可能会比仅仅用目标语言的惯用方式重写效率更低。从Ruby到C的直接转换最多会给您一些非常难看的代码 yield Ruby关键字允许调用作为隐式声明参数传递给方法的代码块。例如,您可以认为smart_get方法定义实际上更像:
def smart_get(key, ttl = nil, generation_time = 30.seconds, &block)
当你打电话给smart_get时:
x = smart_get("mykey", my_ttl) { do_some_operation_here }
大括号中的内容将分配给上面扩展定义中的变量块。然后,yield调用&block中的代码。这是一个粗略的简化,但应该有助于您了解总体思路
回到你的转换。我刚才所做的简化并不一定能让你100%地理解,因为一旦你找到一种C#方式来翻译代码,另一段代码就会打断你的翻译。例如,假设给定的方法需要检查块:
def foo
if block.arity == 0
# No arguments passed, load defaults from config file and ignore call
else
yield
end
end
当然,由于lambda是Ruby中的一流对象,因此您可能会遇到如下代码:
foo = lambda { |a, b, c| a + b + c }
# foo is now defined as a function that sums its three arguments
如果您遇到定义动态方法的代码,或者(更糟糕的是,对于翻译器)利用Ruby的可扩展类,那么上帝会帮助您:
class Foo
def show
puts "Foo"
end
end
foo = Foo.new
foo.show # prints "Foo"
class <<foo; def show; puts "Bar"; end; end
foo.show # prints "Bar"
Foo.new.show # prints "Foo"
foo.show # Still prints "Bar"
class-Foo
def秀
放“福”
结束
结束
foo=foo.new
foo.show#打印“foo”
同学们我一点也不懂C,所以我所说的关于C的任何话都应该被全盘接受。然而,我将尝试解释这段Ruby代码中发生了什么
class << Cache
在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)
这个方法有三个参数(实际上有四个,我们稍后会看到确切的原因),其中两个是可选的(ttl
和generation\u 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
这包含几个复杂的Ruby构造。让我们从最简单的一个开始:尾部条件修饰符。如果条件体仅包含一个表达式,则可以将该条件附加到表达式的末尾。因此,与其说如果a>b那么foo end
,不如说foo如果a>b
。因此,除非ttl然后返回get(key){yield}end
,否则上述操作相当于
下一个问题也很简单:,除非
只是的语法糖,如果不是
。因此,我们现在在如果不是ttl,那么返回get(key){yield}end
第三是鲁比的真理体系。在Ruby中,真相非常简单。事实上,谬误很简单,真理自然就会显现出来:特殊关键字false
是假的,特殊关键字nil
是假的,其他一切都是真的。因此,在这种情况下,只有当ttl
为false
或nil
时,条件才会为truefalse对于时间跨度来说不是一个非常合理的值,因此唯一有趣的值是nil # 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
end
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);
}
def foo
if block.arity == 0
# No arguments passed, load defaults from config file and ignore call
else
yield
end
end