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
相关产品推荐
相关产品推荐

