ฉันใช้การไตร่ตรองเพื่อวนรอบType
คุณสมบัติของและตั้งค่าบางประเภทเป็นค่าเริ่มต้น ตอนนี้ฉันสามารถสลับกับชนิดและตั้งค่าได้default(Type)
อย่างชัดเจน แต่ฉันควรทำในบรรทัดเดียว มีค่าเริ่มต้นเทียบเท่าโปรแกรมหรือไม่
ฉันใช้การไตร่ตรองเพื่อวนรอบType
คุณสมบัติของและตั้งค่าบางประเภทเป็นค่าเริ่มต้น ตอนนี้ฉันสามารถสลับกับชนิดและตั้งค่าได้default(Type)
อย่างชัดเจน แต่ฉันควรทำในบรรทัดเดียว มีค่าเริ่มต้นเทียบเท่าโปรแกรมหรือไม่
คำตอบ:
public static object GetDefault(Type type)
{
if(type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
ใน. net รุ่นใหม่กว่าเช่น. net standard type.IsValueType
จำเป็นต้องเขียนเป็นtype.GetTypeInfo().IsValueType
default(T) != (T)(object)default(T) && !(default(T) != default(T))
คุณมีข้อโต้แย้งมิฉะนั้นมันไม่สำคัญว่าจะเป็นชนิดบรรจุกล่องหรือไม่เนื่องจากพวกเขาจะเทียบเท่า
default(T) != default(T)
คืนค่าผิด ๆ ได้และนั่นก็คือการโกง! =)
Array.CreateInstance(type, length)
ชนิดที่กำหนดและคุณจะได้รับโดยใช้
ทำไมไม่เรียกวิธีการที่ส่งกลับค่าเริ่มต้น (T) ด้วยการสะท้อน? คุณสามารถใช้ GetDefault ประเภทใดก็ได้ด้วย:
public object GetDefault(Type t)
{
return this.GetType().GetMethod("GetDefaultGeneric").MakeGenericMethod(t).Invoke(this, null);
}
public T GetDefaultGeneric<T>()
{
return default(T);
}
nameof(GetDefaultGeneric)
แทน"GetDefaultGeneric"
PropertyInfo.SetValue(obj, null)
คุณสามารถใช้ หากเรียกใช้กับประเภทค่ามันจะให้ค่าเริ่มต้นแก่คุณ ลักษณะการทำงานนี้เป็นเอกสารใน .NET 4.0และใน .NET 4.5
หากคุณใช้. NET 4.0 ขึ้นไปและคุณต้องการรุ่นที่เป็นโปรแกรมที่ไม่ได้เป็นรหัสของกฎที่กำหนดไว้ด้านนอกของรหัสคุณสามารถExpression
สร้างคอมไพล์และเรียกใช้งานได้ทันที
วิธีการขยายต่อไปนี้จะใช้เวลาType
และรับค่าที่ส่งคืนจากdefault(T)
ผ่านDefault
วิธีการในExpression
ชั้นเรียน:
public static T GetDefaultValue<T>()
{
// We want an Func<T> which returns the default.
// Create that expression here.
Expression<Func<T>> e = Expression.Lambda<Func<T>>(
// The default value, always get what the *code* tells us.
Expression.Default(typeof(T))
);
// Compile and return the value.
return e.Compile()();
}
public static object GetDefaultValue(this Type type)
{
// Validate parameters.
if (type == null) throw new ArgumentNullException("type");
// We want an Func<object> which returns the default.
// Create that expression here.
Expression<Func<object>> e = Expression.Lambda<Func<object>>(
// Have to convert to object.
Expression.Convert(
// The default value, always get what the *code* tells us.
Expression.Default(type), typeof(object)
)
);
// Compile and return the value.
return e.Compile()();
}
คุณควรแคชค่าข้างต้นตามType
ด้วย แต่โปรดระวังหากคุณเรียกใช้Type
อินสแตนซ์นี้เป็นจำนวนมากและไม่ใช้อย่างต่อเนื่องหน่วยความจำที่แคชใช้อาจมีประโยชน์มากกว่า
e.Compile()
ถ้าคุณแคช นั่นคือจุดรวมของการแสดงออก
e.Compile()
ควรจะเก็บไว้ แต่สมมติว่าวิธีการนี้คือประมาณ 14 long
เท่าเป็นอย่างรวดเร็วสำหรับเช่น ดูgist.github.com/pvginkel/fed5c8512b9dfefc2870c6853bbfbf8bเพื่อดูเกณฑ์มาตรฐานและผลลัพธ์
e.Compile()
มากกว่าe.Compile()()
? เช่นประเภทเริ่มต้นของประเภทสามารถเปลี่ยนที่รันไทม์? หากไม่ใช่ (เพราะฉันเชื่อว่าเป็นกรณีนี้) คุณสามารถเก็บแคชผลลัพธ์แทนนิพจน์ที่คอมไพล์ซึ่งควรปรับปรุงประสิทธิภาพต่อไป
ทำไมคุณถึงบอกว่ายาชื่อสามัญไม่ได้อยู่ในภาพ?
public static object GetDefault(Type t)
{
Func<object> f = GetDefault<object>;
return f.Method.GetGenericMethodDefinition().MakeGenericMethod(t).Invoke(null, null);
}
private static T GetDefault<T>()
{
return default(T);
}
นี่คือโซลูชันของ Flem ที่ได้รับการปรับปรุง:
using System.Collections.Concurrent;
namespace System
{
public static class TypeExtension
{
//a thread-safe way to hold default instances created at run-time
private static ConcurrentDictionary<Type, object> typeDefaults =
new ConcurrentDictionary<Type, object>();
public static object GetDefaultValue(this Type type)
{
return type.IsValueType
? typeDefaults.GetOrAdd(type, Activator.CreateInstance)
: null;
}
}
}
return type.IsValueType ? typeDefaults.GetOrAdd(type, Activator.CreateInstance) : null;
คำตอบที่เลือกนั้นเป็นคำตอบที่ดี แต่ระวังด้วยการส่งคืนวัตถุ
string test = null;
string test2 = "";
if (test is string)
Console.WriteLine("This will never be hit.");
if (test2 is string)
Console.WriteLine("Always hit.");
คะเน ...
string test = GetDefault(typeof(string));
if (test is string)
Console.WriteLine("This will never be hit.");
นิพจน์สามารถช่วยได้ที่นี่:
private static Dictionary<Type, Delegate> lambdasMap = new Dictionary<Type, Delegate>();
private object GetTypedNull(Type type)
{
Delegate func;
if (!lambdasMap.TryGetValue(type, out func))
{
var body = Expression.Default(type);
var lambda = Expression.Lambda(body);
func = lambda.Compile();
lambdasMap[type] = func;
}
return func.DynamicInvoke();
}
ฉันไม่ได้ทดสอบตัวอย่างนี้ แต่ฉันคิดว่าควรสร้าง "null" ที่พิมพ์สำหรับประเภทการอ้างอิง ..
"typed" nulls
- อธิบาย คุณจะกลับวัตถุอะไร ถ้าคุณกลับวัตถุของการพิมพ์type
แต่ค่าของมันเป็นnull
แล้วมันไม่ได้ - ไม่สามารถ - null
มีข้อมูลอื่นใดนอกเหนือจากที่มันเป็น คุณไม่สามารถสืบค้นnull
ค่าและค้นหาประเภทที่คาดคะเน หากคุณไม่กลับมาเป็นโมฆะ แต่กลับมา .. ฉันไม่รู้ว่า .. แล้วมันจะไม่เป็นเช่นnull
นั้น
ยังหาสิ่งที่เรียบง่ายและสง่างามไม่ได้ แต่ฉันมีความคิดอย่างหนึ่ง: หากคุณทราบประเภทของอสังหาริมทรัพย์ที่คุณต้องการตั้งค่าคุณสามารถเขียนเองdefault(T)
ได้ มีสองกรณี - T
เป็นประเภทค่าและT
เป็นประเภทอ้างอิง T.IsValueType
คุณสามารถดูนี้โดยการตรวจสอบ ถ้าเป็นชนิดอ้างอิงแล้วคุณก็สามารถตั้งค่าให้T
null
หากT
เป็นประเภทค่าจะมีตัวสร้างแบบไร้พารามิเตอร์ที่คุณสามารถเรียกเพื่อรับค่า "blank"
ฉันทำสิ่งเดียวกันนี้
//in MessageHeader
private void SetValuesDefault()
{
MessageHeader header = this;
Framework.ObjectPropertyHelper.SetPropertiesToDefault<MessageHeader>(this);
}
//in ObjectPropertyHelper
public static void SetPropertiesToDefault<T>(T obj)
{
Type objectType = typeof(T);
System.Reflection.PropertyInfo [] props = objectType.GetProperties();
foreach (System.Reflection.PropertyInfo property in props)
{
if (property.CanWrite)
{
string propertyName = property.Name;
Type propertyType = property.PropertyType;
object value = TypeHelper.DefaultForType(propertyType);
property.SetValue(obj, value, null);
}
}
}
//in TypeHelper
public static object DefaultForType(Type targetType)
{
return targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
}
เทียบเท่ากับคำตอบของ Dror แต่เป็นวิธีการขยาย:
namespace System
{
public static class TypeExtensions
{
public static object Default(this Type type)
{
object output = null;
if (type.IsValueType)
{
output = Activator.CreateInstance(type);
}
return output;
}
}
}
การปรับเปลี่ยนเล็กน้อยสำหรับโซลูชันของ @Rob Fonseca-Ensor : วิธีการขยายต่อไปนี้ทำงานบน. Net Standard เนื่องจากฉันใช้ GetRuntimeMethod แทน GetMethod
public static class TypeExtensions
{
public static object GetDefault(this Type t)
{
var defaultValue = typeof(TypeExtensions)
.GetRuntimeMethod(nameof(GetDefaultGeneric), new Type[] { })
.MakeGenericMethod(t).Invoke(null, null);
return defaultValue;
}
public static T GetDefaultGeneric<T>()
{
return default(T);
}
}
... และการทดสอบตามหน่วยสำหรับผู้ที่สนใจเกี่ยวกับคุณภาพ:
[Fact]
public void GetDefaultTest()
{
// Arrange
var type = typeof(DateTime);
// Act
var defaultValue = type.GetDefault();
// Assert
defaultValue.Should().Be(default(DateTime));
}
/// <summary>
/// returns the default value of a specified type
/// </summary>
/// <param name="type"></param>
public static object GetDefault(this Type type)
{
return type.IsValueType ? (!type.IsGenericType ? Activator.CreateInstance(type) : type.GenericTypeArguments[0].GetDefault() ) : null;
}
Nullable<T>
ประเภท: มันไม่ได้กลับมาเทียบเท่าของที่ควรจะเป็นdefault(Nullable<T>)
null
คำตอบที่ได้รับการยอมรับจาก Dror ทำงานได้ดีขึ้น
สิ่งนี้น่าจะใช้ได้:
Nullable<T> a = new Nullable<T>().GetValueOrDefault();