You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin.Essentials Preferences泛型封装SetStorage报错如何解决

问题背景

我正在尝试为Xamarin.Essentials中的Preferences类编写封装。
Preferences类的Set方法提供了多个重载,支持传入string/int/bool/DateTime/long/double/float类型的值参数,我希望为其实现通用泛型封装。

方法签名参考:Preferences.Set(string key, [对应类型] value)

当前实现代码如下,其中GetStorage方法可正常运行,但SetStorage方法触发编译报错:

public class StorageService: IStorageService
{
    public T GetStorage<T>(string key)
    {
        object value = Preferences.Get(key,null);
        return (T)Convert.ChangeType(value, typeof(T));
    }

    public void SetStorage<T>(string key, T value)
    {
        Preferences.Set(key, value); // 此处编译报错
    }
}

需要修改代码让泛型存储功能正常运行。

报错原因

C#的方法重载决策在编译阶段完成,未加类型约束的泛型参数T在编译时类型不明确,编译器无法将其匹配到Preferences.Set的任意一个固定类型参数重载上,因此抛出编译错误。
Xamarin.Essentials.Preferences本身没有提供接收object类型参数的Set重载,无法直接传入泛型参数。

修复方案

根据使用场景二选一即可:

方案1:类型分支分派(仅支持原生支持的基础类型,无额外性能开销)

针对Preferences原生支持的所有值类型做类型判断,分派到对应的重载方法,同时补充参数校验和不支持类型的异常提示:

public void SetStorage<T>(string key, T value)
{
    if (string.IsNullOrWhiteSpace(key))
        throw new ArgumentException("存储键不能为空", nameof(key));
    if (value == null)
        throw new ArgumentNullException(nameof(value));

    switch (value)
    {
        case string strVal:
            Preferences.Set(key, strVal);
            break;
        case int intVal:
            Preferences.Set(key, intVal);
            break;
        case bool boolVal:
            Preferences.Set(key, boolVal);
            break;
        case DateTime dtVal:
            Preferences.Set(key, dtVal);
            break;
        case long longVal:
            Preferences.Set(key, longVal);
            break;
        case double doubleVal:
            Preferences.Set(key, doubleVal);
            break;
        case float floatVal:
            Preferences.Set(key, floatVal);
            break;
        default:
            throw new NotSupportedException($"类型{typeof(T).FullName}不支持直接存储,Preferences原生仅支持基础值类型");
    }
}

建议同步优化原有GetStorage方法,补充默认值支持、避免类型转换异常:

public T GetStorage<T>(string key, T defaultValue = default)
{
    if (string.IsNullOrWhiteSpace(key))
        throw new ArgumentException("存储键不能为空", nameof(key));

    object storedValue = defaultValue switch
    {
        string s => Preferences.Get(key, s),
        int i => Preferences.Get(key, i),
        bool b => Preferences.Get(key, b),
        DateTime dt => Preferences.Get(key, dt),
        long l => Preferences.Get(key, l),
        double d => Preferences.Get(key, d),
        float f => Preferences.Get(key, f),
        _ => throw new NotSupportedException($"类型{typeof(T).FullName}不支持直接读取")
    };

    return (T)Convert.ChangeType(storedValue, typeof(T));
}

方案2:序列化存储(支持任意可序列化类型,适合存储自定义对象)

如果需要存储自定义类、结构体等复杂类型,可以先将值序列化为JSON字符串,再调用string类型的Set重载存储,读取时反序列化即可:

// 需提前引入System.Text.Json序列化库
public void SetStorage<T>(string key, T value)
{
    if (string.IsNullOrWhiteSpace(key))
        throw new ArgumentException("存储键不能为空", nameof(key));
    
    var jsonStr = JsonSerializer.Serialize(value);
    Preferences.Set(key, jsonStr);
}

public T GetStorage<T>(string key, T defaultValue = default)
{
    if (string.IsNullOrWhiteSpace(key))
        throw new ArgumentException("存储键不能为空", nameof(key));
    
    var jsonStr = Preferences.Get(key, string.Empty);
    if (string.IsNullOrEmpty(jsonStr))
        return defaultValue;
    
    return JsonSerializer.Deserialize<T>(jsonStr);
}

注意:该方案存储基础类型时会产生额外的序列化性能开销,若仅需存储原生支持的基础值类型,优先选择方案1。

内容的提问来源于stack exchange,提问作者Shubham Tyagi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 09:54:22