你能避免在memoizing泛型方法中取消装箱吗?

时间:2011-01-19 01:18:11

标签: c# generics boxing memoization

我有一个泛型方法,用于记忆db中字符串值的转换为实际转换值。

public MySpecialValue {
    object val;
    bool valSet = false; 

    T GetValue<T> () { 
         if (!valSet)
         {
                val = (T)Convert.ChangeType(DatabaseValue, typeof(T));
                valSet = true;
         }
         return (T)val;
     }

     public string DatabaseValue { get; set; }
}

问题是在初始化期间我不知道数据库中的数据是什么类型,它只能在第一次调用时才能做出这个决定。

有没有办法以这样的方式构造它,以至于它不被强制为缓存的unbox值类型? (不改变包含类的签名)

5 个答案:

答案 0 :(得分:5)

你提供的代码有些奇怪。 “DoSomethingExpensive”如何知道为任意T 返回T ?这对我来说毫无意义。

您通常编写通用记事本的方式如下:

public static Func<T> Memoize(this Func<T> func)
{
    bool executed = false;
    T result = default(T);
    Func<T> memoized = ()=> 
    {
        if (!executed)
        {
            result = func();
            executed = true;
        }
        return result;
    };
    return memoized;
}

现在你可以说:

Func<int> expensive = DoSomethingExpensiveThatGetsAnInt;
Func<int> memoized = expensive.Memoize();

你已经完成了。不需要拳击。

答案 1 :(得分:3)

如果T和Convert.ChangeType的结果是引用类型,则不会取消装箱。

如果Convert.ChangeType返回一个盒装值类型而T是一个值类型,那么如果你想让GetValue返回T,你就无法做任何事情来避免拆箱。

答案 2 :(得分:1)

考虑使用Lazy<T>

所以如果你有某种属性集合。

public class MyClass 
{
...
}

Public class MyClass<T> : MyClass
{
  T val;
  bool valSet; 
  public T GetValue<T> () { 
        if (!valSet)
        {
            val = (T)Convert.ChangeType(DatabaseValue, typeof(T))};
            valSet = true;
        }
        return val;
    }
}

据推测,您的父类中有一个通用方法

class SomePropertyBag{

private Dictionary<string, MyClass> dict;

T GetValue<T>(string name, T default)
{
  MyClass res;
  if(!dict.TryGetValue(out res))
  {
     res = new MyClass<T>(name);
     dict.Add(name, res);
  }
  return ((MyClass<T>)res).GetValue();
}

答案 3 :(得分:1)

我愿意打赌(好吧,我也有使用源代码的优势......)在大多数情况下,这段代码会根据您your reply

中的示例进行调用
Get("Site.Twitter.AccountName", "")

Get("Site.Twitter.AccountName", 77)

在这种情况下,您使用的是泛型类型推断。但还有另一个更简单的东西可以在那里编译...... 不要使用泛型。我希望这里只有几个场景;所以一些类/方法重载 - 一个用于string,一个用于int等等。

string Get(string key, string defaultValue) {...}
int Get(string key, int defaultValue) {...}
bool Get(string key, bool defaultValue) {...}

当然,会有一个重复,但编译器将能够针对每个单独的场景进行优化 - 没有装箱。你甚至可以(如果你选择的话)用Convert.ChangeType替换int.Parse(对于T = int案例)。


另一个选项(给出你的例子)是使memoized对象通用:

public MySpecialValue<T> {
    T val;
    bool valSet = false; 

    T GetValue() { 
         if (!valSet)
         {
                val = (T)Convert.ChangeType(DatabaseValue, typeof(T));
                valSet = true;
         }
         return val;
     }

     public string DatabaseValue { get; set; }
}

并将<T>代码提升到某个级别,以便在演员阵容中完成。

答案 4 :(得分:0)

这使得对于值类型(没有取消装箱)的速度变得非常快......并且对于ref类型一次额外的调用来说速度稍慢......但是有点奇怪。

 class MyClass {
    class Container<T>
    {
        public T Value { get; set; }
    }

    bool valSet;
    object val; 

    public T GetValue<T> () { 
        if (!valSet)
        {
            val = new Container<T>{Value =  (T)Convert.ChangeType(DatabaseValue, typeof(T))};
            valSet = true;
        }
        return ((Container<T>)val).Value;
    }

    public string DatabaseValue { get; set; }
 }