[英]How to write generic extension method which with one type argument for generic class with another type argument
我有以下界面
public interface ITypedValueProvider<TValue>
{
TValue GetValue(Type type);
}
我想創建擴展方法,它允許我從泛型類型參數中獲取類型。 我寫了一個如下的擴展方法。
public static class Extensions
{
public static TValue GetValue<TType, TValue>(this ITypedValueProvider<TValue> valueProvider)
{
return valueProvider.GetValue(typeof(TType));
}
}
但是,此擴展方法需要 2 個類型 arguments,我只需要使用一個,希望其他可以從輸入泛型 class 中推斷出來。
static class Program
{
void Main()
{
ITypedValueProvider<int> valueProvider = new ValueProvider<int>(typeof(TargetValueConsumer));
var value = valueProvider.GetValue<TargetValueConsumer, int>() //This line will work
var value = valueProvider.GetValue<TargetValueConsumer>(); //This one, obviously, won't compile
}
}
public class ValueProvider<TValue> : ITypedValueProvider<TValue>
{
private readonly (Type ConsumerType, TValue Value) _record;
public ValueProvider(TValue value, Type type)
{
_record = (type, value);
}
TValue GetValue(Type type)
{
if (_record.ConsumerType == type)
return _record.Value;
return default;
}
}
public class TargetValueConsumer {}
我如何編寫不需要第二類型參數的擴展方法,或者如何使用現有方法從用法中推斷第二類型參數?
要在這里回答您的具體情況,您的擴展方法不需要 2 個通用參數。 不需要 TType ,因為在您的情況下 TType 和 TValue 將始終相同。 只需擺脫 TType 參數並將typeof(TValue)
傳遞給如下方法
public static TValue GetValue<TValue>(this ITypedValueProvider<TValue> valueProvider)
{
return valueProvider.GetValue(typeof(TValue));
}
C# 無法推斷 TType,因為從中可以看出它是一個完整的無關類型。
TValue 是 ITypedValueProvider 的類型參數和返回類型。TType 只是一個“獨立”類型參數,因此您需要指定它。
但是就像我說的,你顯然希望 TType 和 TValue 是相同的類型,所以不要為 TType 煩惱,所做的只是讓你需要指定什么感覺像是一個不需要的類型參數,並且讓你容易遇到錯誤,比如
ITypedValueProvider<string> valueProvider = GetValueProvider();
valueProvider.GetValue<int>(); //what would this end up doing, definately not what you want though
我有一個類似的用例,這對我有用。 可能有一些你可以使用的東西。 您要做的是對ITypedValueProvider
進行擴展,並在擴展this
中從中推斷調用者的泛型類型。
接口不需要是通用的:
public interface ITypedValueProvider
{
TValue GetValue<TValue>();
}
你說你想要一個不同 T 的通用 class。給你 go:
class TargetValueBase<TClass> : TargetValueBase, ITypedValueProvider { }
創建一個實例:
var consumer = new TargetValueConsumer<AppDomain>();
這樣您就可以調用(例如):
consumer.LogValueForClass<XElement>()
示例擴展方法(您不能直接在 class 上調用的方法)可能類似於:
public static class Extensions
{
public static string LogValueForClass<TValue>(this ITypedValueProvider @this)
{
var type = @this.GetType();
var builder = new List<string>();
builder.Add($"INFERRED CLASS: { type.GetGenericTypeDefinition().Name.Split('`')[0]}" );
foreach (var arg in type.GetGenericArguments())
{
builder.Add(arg.Name);
}
// Call a generic method on the provider
var getValueResult = @this.GetValue<TValue>();
builder.Add($"The GetValue method returned: {getValueResult}");
return string.Join(Environment.NewLine, builder);
}
}
TargetValueBase
也不需要是通用的。
class TargetValueBase
{
public TValue GetValue<TValue>()
{
var tValueName = typeof(TValue).Name;
// Figure out what to return.
switch (tValueName)
{
case nameof(XElement):
Console.WriteLine($"Case 1: {tValueName}");
var instance = new XElement("xelement", new XAttribute("hello", "world"));
return (TValue)(object)instance;
case nameof(UInt16):
Console.WriteLine($"Case 2: {tValueName}");
break;
case nameof(UInt32):
Console.WriteLine($"Case 3: {tValueName}");
break;
case nameof(UInt64):
Console.WriteLine($"Case 4: {tValueName}");
break;
}
return default(TValue);
}
public override string ToString() => GetType().Name;
}
把它全部放在一個小的單元測試中......
[TestMethod]
public void GenericGetterTest()
{
string result;
TargetValueBase nonGeneric = new TargetValueBase();
// Calling Generic GetValue doesn't require an extension
// or even a generic class. But that's not what you asked.
Console.WriteLine($"{Environment.NewLine}Calling Generic Method on Non-Generic Value Provider{Environment.NewLine}");
nonGeneric.GetValue<XElement>();
nonGeneric.GetValue<UInt16>();
nonGeneric.GetValue<UInt32>();
nonGeneric.GetValue<UInt64>();
var retail = new TargetValueRetailer<CrossAppDomainDelegate>();
var consumer = new TargetValueConsumer<AppDomain>();
// We make extensions for methods we CAN'T call on the class. Maybe
// the original class is from an external dll, or the class is
// sealed for inheritance, or change all the references is prohibitive.
// What you asked:
// "Generic extension method with one generic type for class and another for value."
// Here's a made-up one:
Console.WriteLine($"{Environment.NewLine}Calling Extension...{Environment.NewLine}");
result = consumer.LogValueForClass<UInt16>();
Console.WriteLine($"{result}{Environment.NewLine}");
Console.WriteLine($"{Environment.NewLine}Calling Extension...{Environment.NewLine}");
result = retail.LogValueForClass<XElement>();
Console.WriteLine($"{retail.LogValueForClass<XElement>()}{Environment.NewLine}");
}
...然后運行它!
樂趣。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.