Có một ràng buộc nào giới hạn phương thức chung của tôi đối với các kiểu số không?


364

Bất cứ ai có thể cho tôi biết nếu có một cách với generic để giới hạn một đối số loại chung chung Tchỉ:

  • Int16
  • Int32
  • Int64
  • UInt16
  • UInt32
  • UInt64

Tôi biết wheretừ khóa, nhưng không thể tìm thấy giao diện chỉ các loại này,

Cái gì đó như:

static bool IntegerFunction<T>(T value) where T : INumeric 

2
Hiện tại có nhiều đề xuất C # khác nhau sẽ cho phép thực hiện điều này, nhưng AFAIK không có đề xuất nào ngoài những khám phá / thảo luận sơ bộ. Xem Thăm dò: Hình dạng và Phần mở rộng , Thăm dò: Vai trò, giao diện mở rộng và thành viên giao diện tĩnh , Loại lớp "(còn gọi là khái niệm, ràng buộc chung về cấu trúc)"Đề xuất: Các loại chung nên hỗ trợ các nhà khai thác
Chris Yungmann 17/10/19

Câu trả lời:


140

C # không hỗ trợ này. Hejlsberg đã mô tả các lý do không thực hiện tính năng này trong một cuộc phỏng vấn với Bruce Eckel :

Và không rõ ràng rằng sự phức tạp thêm vào đó xứng đáng với sản lượng nhỏ mà bạn nhận được. Nếu một cái gì đó bạn muốn làm không được hỗ trợ trực tiếp trong hệ thống ràng buộc, bạn có thể làm điều đó với một mẫu nhà máy. Bạn có thể có một Matrix<T>ví dụ, và trong đó Matrixbạn muốn xác định một phương thức sản phẩm chấm. Điều đó tất nhiên là phương tiện cuối cùng bạn cần phải hiểu làm thế nào để nhân hai Ts, nhưng bạn không thể nói rằng là một hạn chế, ít nhất là không nếu Tint, doublehoặc float. Nhưng những gì bạn có thể làm là Matrixlấy tham số của bạn làm phương thức a Calculator<T>, và trong Calculator<T>, có một phương thức được gọi multiply. Bạn thực hiện điều đó và bạn chuyển nó cho Matrix.

Tuy nhiên, điều này dẫn đến mã khá phức tạp, nơi người dùng phải cung cấp Calculator<T>triển khai của riêng họ , cho mỗi mã Tmà họ muốn sử dụng. Chừng nào nó không phải là mở rộng, tức là nếu bạn chỉ muốn hỗ trợ một số cố định các loại, chẳng hạn như intdouble, bạn có thể nhận được ngay với một giao diện tương đối đơn giản:

var mat = new Matrix<int>(w, h);

( Thực hiện tối thiểu trong một GitHub Gist. )

Tuy nhiên, ngay khi bạn muốn người dùng có thể cung cấp các loại tùy chỉnh của riêng họ, bạn cần mở triển khai này để người dùng có thể cung cấp các Calculatorphiên bản của riêng họ . Chẳng hạn, để khởi tạo một ma trận sử dụng triển khai dấu phẩy động thập phân tùy chỉnh DFP, bạn phải viết mã này:

var mat = new Matrix<DFP>(DfpCalculator.Instance, w, h);

Càng và thực hiện tất cả các thành viên cho DfpCalculator : ICalculator<DFP> .

Một giải pháp thay thế, không may chia sẻ những hạn chế tương tự, là làm việc với các lớp chính sách, như được thảo luận trong câu trả lời của Serge Shandar .


25
btw, MiscUtil cung cấp một lớp chung thực hiện chính xác điều này; Operator/ Operator<T>; yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html
Marc Gravell

1
@Mark: bình luận tốt. Tuy nhiên, để rõ ràng, tôi không nghĩ rằng Hejlsberg đã coi việc tạo mã là một giải pháp cho vấn đề như bạn làm trong Operator<T>mã (vì cuộc phỏng vấn đã được đưa ra từ lâu trước khi có sự tồn tại của Expressionskhung, mặc dù người ta có thể sử dụng khóa học Reflection.Emit) - và tôi thực sự quan tâm đến cách giải quyết của anh ấy .
Konrad Rudolph

@Konrad Rudolph: Tôi nghĩ câu trả lời này cho một câu hỏi tương tự giải thích cách giải quyết của Hejlsberg. Các lớp chung khác được thực hiện trừu tượng. Vì nó yêu cầu bạn triển khai lớp chung khác cho từng loại bạn muốn hỗ trợ, nên nó sẽ dẫn đến mã trùng lặp, nhưng điều đó có nghĩa là bạn chỉ có thể khởi tạo lớp chung gốc với loại được hỗ trợ.
Ergwun

14
Tôi không đồng ý với câu nói của Heijsberg "Vì vậy, theo một nghĩa nào đó, các mẫu C ++ thực sự được tháo ra hoặc được gõ một cách lỏng lẻo. Trong khi đó, các tổng quát của C # được gõ mạnh." Đó thực sự là BS tiếp thị để quảng bá C #. Gõ mạnh / yếu không liên quan đến chất lượng chẩn đoán. Mặt khác: Tìm thú vị.
Sebastian Mach

100

Xem xét mức độ phổ biến của câu hỏi này và sự quan tâm đằng sau một chức năng như vậy, tôi ngạc nhiên khi thấy rằng chưa có câu trả lời nào liên quan đến T4.

Trong mã mẫu này, tôi sẽ trình bày một ví dụ rất đơn giản về cách bạn có thể sử dụng công cụ tạo khuôn mẫu mạnh mẽ để thực hiện những gì trình biên dịch thực hiện khá nhiều trong hậu trường với các tổng quát.

Thay vì trải qua các vòng và hy sinh sự chắc chắn về thời gian biên dịch, bạn có thể chỉ cần tạo hàm bạn muốn cho mọi loại bạn thích và sử dụng theo đó (tại thời gian biên dịch!).

Để làm điều này:

  • Tạo một tệp Mẫu văn bản mới có tên GenericNumberMethodTemplate.tt .
  • Xóa mã được tạo tự động (bạn sẽ giữ phần lớn mã, nhưng một số không cần thiết).
  • Thêm đoạn mã sau:
<#@ template language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>

<# Type[] types = new[] {
    typeof(Int16), typeof(Int32), typeof(Int64),
    typeof(UInt16), typeof(UInt32), typeof(UInt64)
    };
#>

using System;
public static class MaxMath {
    <# foreach (var type in types) { 
    #>
        public static <#= type.Name #> Max (<#= type.Name #> val1, <#= type.Name #> val2) {
            return val1 > val2 ? val1 : val2;
        }
    <#
    } #>
}

Đó là nó. Bạn đã hoàn thành

Lưu tệp này sẽ tự động biên dịch nó vào tệp nguồn này:

using System;
public static class MaxMath {
    public static Int16 Max (Int16 val1, Int16 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static Int32 Max (Int32 val1, Int32 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static Int64 Max (Int64 val1, Int64 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt16 Max (UInt16 val1, UInt16 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt32 Max (UInt32 val1, UInt32 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt64 Max (UInt64 val1, UInt64 val2) {
        return val1 > val2 ? val1 : val2;
    }
}

Trong mainphương pháp của bạn, bạn có thể xác minh rằng bạn có sự chắc chắn về thời gian biên dịch:

namespace TTTTTest
{
    class Program
    {
        static void Main(string[] args)
        {
            long val1 = 5L;
            long val2 = 10L;
            Console.WriteLine(MaxMath.Max(val1, val2));
            Console.Read();
        }
    }
}

nhập mô tả hình ảnh ở đây

Tôi sẽ đi trước một nhận xét: không, đây không phải là vi phạm nguyên tắc DRY. Nguyên tắc DRY là có để ngăn chặn mọi người sao chép mã ở nhiều nơi sẽ khiến ứng dụng trở nên khó bảo trì.

Đây không phải là trường hợp ở đây: nếu bạn muốn thay đổi thì bạn chỉ có thể thay đổi mẫu (một nguồn duy nhất cho tất cả thế hệ của bạn!) Và thế là xong.

Để sử dụng nó với các định nghĩa tùy chỉnh của riêng bạn, hãy thêm một khai báo không gian tên (đảm bảo nó giống với định nghĩa nơi bạn sẽ xác định triển khai của riêng mình) vào mã được tạo và đánh dấu lớp là partial. Sau đó, thêm các dòng này vào tệp mẫu của bạn để nó sẽ được bao gồm trong phần tổng hợp cuối cùng:

<#@ import namespace="TheNameSpaceYouWillUse" #>
<#@ assembly name="$(TargetPath)" #>

Hãy trung thực: Điều này là khá mát mẻ.

Tuyên bố miễn trừ trách nhiệm: mẫu này đã bị ảnh hưởng nặng nề bởi Metaprogramming trong .NET bởi Kevin Hazzard và Jason Bock, Manning Publications .


Điều này khá thú vị, nhưng liệu có thể sửa đổi giải pháp này để làm cho các phương thức chấp nhận một số loại chung chung Thoặc kế thừa từ các IntXlớp khác nhau ? Tôi thích giải pháp này vì nó tiết kiệm thời gian, nhưng để giải quyết vấn đề 100% (mặc dù không tốt như thể C # có hỗ trợ cho loại ràng buộc này, tích hợp sẵn), mỗi phương thức được tạo vẫn phải chung chung để họ có thể trả về một đối tượng thuộc kiểu kế thừa từ một trong các IntXXlớp.
Zachary Kniebel 17/03/2016

1
@ZacharyKniebel: các IntXXloại là cấu trúc có nghĩa là chúng không hỗ trợ thừa kế ở vị trí đầu tiên . Và ngay cả khi nó đã thực hiện thì nguyên tắc thay thế Liskov (mà bạn có thể biết từ thành ngữ RẮN) được áp dụng: nếu phương thức được định nghĩa là XYlà con của Xmỗi định nghĩa thì bất kỳ định nghĩa nào Ycũng có thể được truyền cho phương thức đó thay thế cho loại cơ sở của nó.
Jeroen Vannevel 17/03/2016

1
Cách giải quyết này bằng cách sử dụng các chính sách stackoverflow.com/questions/32664/ cho sử dụng T4 để tạo các lớp.
Serge Shandar

2
+1 cho giải pháp này vì nó bảo toàn hiệu quả hoạt động của các loại tích phân tích hợp, không giống như các giải pháp dựa trên chính sách. Gọi các toán tử CLR tích hợp (như Thêm) thông qua một phương thức bổ sung (có thể là ảo) có thể ảnh hưởng nghiêm trọng đến hiệu suất nếu được sử dụng nhiều lần (như trong các thư viện toán học). Và vì số lượng các loại tích phân là không đổi (và không thể được kế thừa từ đó), bạn chỉ cần tạo lại mã để sửa lỗi.
Attila Klenik

1
Rất tuyệt và tôi vừa mới bắt đầu sử dụng thì tôi đã nhớ rằng tôi phụ thuộc vào Resharper như thế nào để tái cấu trúc và bạn không thể đổi tên refactor thông qua mẫu T4. Nó không quan trọng nhưng đáng xem xét.
bradgonesurfing

86

Không có ràng buộc cho điều này. Đây là một vấn đề thực sự cho bất cứ ai muốn sử dụng thuốc generic để tính toán số.

Tôi sẽ đi xa hơn và nói rằng chúng tôi cần

static bool GenericFunction<T>(T value) 
    where T : operators( +, -, /, * )

Hoặc thậm chí

static bool GenericFunction<T>(T value) 
    where T : Add, Subtract

Thật không may, bạn chỉ có giao diện, lớp cơ sở và từ khóa struct(phải là loại giá trị), class(phải là loại tham chiếu) và new()(phải có hàm tạo mặc định)

Bạn có thể bọc số trong một cái gì đó khác (tương tự INullable<T>) như ở đây trên bảng mã .


Bạn có thể áp dụng các hạn chế trong thời gian chạy (bằng cách phản ánh cho các toán tử hoặc kiểm tra các loại) nhưng điều đó sẽ làm mất lợi thế của việc có chung chung ở vị trí đầu tiên.


2
Tôi tự hỏi nếu bạn đã thấy sự hỗ trợ của MiscUtil cho các nhà khai thác chung ... yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html
Marc Gravell

10
Vâng - Jon Skeet đã chỉ cho tôi một cái gì đó cách đây một thời gian trước đây (nhưng sau phản ứng năm nay) - chúng là một ý tưởng thông minh, nhưng tôi vẫn thích hỗ trợ ràng buộc thích hợp.
Keith

1
Đợi đã, where T : operators( +, -, /, * )C # hợp pháp? Xin lỗi cho câu hỏi của người mới.
kdbanman

@kdbanman Mình không nghĩ vậy. Keith đang nói rằng C # không hỗ trợ những gì OP đang yêu cầu và đang gợi ý rằng chúng tôi sẽ có thể làm được where T : operators( +, -, /, * ), nhưng không thể.
AMTerp

62

Giải pháp sử dụng chính sách:

interface INumericPolicy<T>
{
    T Zero();
    T Add(T a, T b);
    // add more functions here, such as multiplication etc.
}

struct NumericPolicies:
    INumericPolicy<int>,
    INumericPolicy<long>
    // add more INumericPolicy<> for different numeric types.
{
    int INumericPolicy<int>.Zero() { return 0; }
    long INumericPolicy<long>.Zero() { return 0; }
    int INumericPolicy<int>.Add(int a, int b) { return a + b; }
    long INumericPolicy<long>.Add(long a, long b) { return a + b; }
    // implement all functions from INumericPolicy<> interfaces.

    public static NumericPolicies Instance = new NumericPolicies();
}

Thuật toán:

static class Algorithms
{
    public static T Sum<P, T>(this P p, params T[] a)
        where P: INumericPolicy<T>
    {
        var r = p.Zero();
        foreach(var i in a)
        {
            r = p.Add(r, i);
        }
        return r;
    }

}

Sử dụng:

int i = NumericPolicies.Instance.Sum(1, 2, 3, 4, 5);
long l = NumericPolicies.Instance.Sum(1L, 2, 3, 4, 5);
NumericPolicies.Instance.Sum("www", "") // compile-time error.

Các giải pháp là thời gian biên dịch an toàn. CityLizard Framework cung cấp phiên bản biên dịch cho .NET 4.0. Tệp này là lib / NETFramework4.0 / CityLizard.Policy.dll.

Nó cũng có sẵn trong Nuget: https://www.nuget.org/packages/CityLizard/ . Xem cấu trúc CityLizard.Policy.I .


Tôi gặp vấn đề với mẫu này khi có ít đối số hàm hơn các tham số chung. Đã mở stackoverflow.com/questions/36048248/
Mạnh

bất kỳ lý do tại sao sử dụng struct? nếu tôi sử dụng lớp singleton thay thế và thay đổi thể hiện thành public static NumericPolicies Instance = new NumericPolicies();và sau đó thêm hàm tạo này private NumericPolicies() { }.
M.kazem Akh,

@ M.kazemAkhÿ bạn có thể sử dụng singleton. Tôi thích cấu trúc hơn. Về lý thuyết, nó có thể được tối ưu hóa bởi trình biên dịch / CLR vì cấu trúc không chứa thông tin. Trong trường hợp của singleton, bạn vẫn sẽ vượt qua một tài liệu tham khảo, điều này có thể gây thêm áp lực cho GC. Một lợi thế khác là struct không thể null :-).
Serge Shandar

Tôi sẽ nói rằng bạn đã tìm thấy một giải pháp rất thông minh, nhưng giải pháp đó quá hạn chế đối với tôi: Tôi sẽ sử dụng nó T Add<T> (T t1, T t2), nhưng Sum()chỉ hoạt động khi nó có thể lấy ra loại T của chính nó từ các tham số của nó, điều này là không thể khi nó được nhúng trong một chức năng chung khác.
Tobias Knauss

16

Câu hỏi này là một câu hỏi thường gặp, vì vậy tôi sẽ đăng bài này dưới dạng wiki (vì tôi đã đăng tương tự trước đây, nhưng đây là một câu hỏi cũ hơn); dù sao...

Bạn đang sử dụng phiên bản .NET nào? Nếu bạn đang sử dụng .NET 3.5, thì tôi có một triển khai toán tử chung trong MiscUtil (miễn phí, v.v.).

Điều này có các phương thức như T Add<T>(T x, T y)và các biến thể khác cho số học trên các loại khác nhau (như DateTime + TimeSpan).

Ngoài ra, điều này hoạt động cho tất cả các toán tử sẵn có, nâng và bespoke và lưu trữ cho đại biểu để thực hiện.

Một số nền tảng bổ sung về lý do tại sao điều này là khó khăn ở đây .

Bạn cũng có thể muốn biết rằng dynamic(4.0) sắp xếp giải quyết vấn đề này một cách gián tiếp - tức là

dynamic x = ..., y = ...
dynamic result = x + y; // does what you expect

14

Thật không may, bạn chỉ có thể chỉ định struct trong mệnh đề where trong trường hợp này. Có vẻ lạ khi bạn không thể chỉ định Int16, Int32, v.v. nhưng tôi chắc chắn có một số lý do triển khai sâu sắc dựa trên quyết định không cho phép các loại giá trị trong mệnh đề where.

Tôi đoán giải pháp duy nhất là thực hiện kiểm tra thời gian chạy không may ngăn chặn sự cố được xử lý tại thời điểm biên dịch. Điều đó sẽ đi một cái gì đó như: -

static bool IntegerFunction<T>(T value) where T : struct {
  if (typeof(T) != typeof(Int16)  &&
      typeof(T) != typeof(Int32)  &&
      typeof(T) != typeof(Int64)  &&
      typeof(T) != typeof(UInt16) &&
      typeof(T) != typeof(UInt32) &&
      typeof(T) != typeof(UInt64)) {
    throw new ArgumentException(
      string.Format("Type '{0}' is not valid.", typeof(T).ToString()));
  }

  // Rest of code...
}

Đó là một chút xấu xí tôi biết, nhưng ít nhất cung cấp các ràng buộc cần thiết.

Tôi cũng sẽ xem xét các tác động hiệu suất có thể có cho việc triển khai này, có lẽ có một cách nhanh hơn ngoài kia.


13
+1, tuy nhiên, // Rest of code...có thể không biên dịch nếu nó phụ thuộc vào các hoạt động được xác định bởi các ràng buộc.
Nick

1
Convert.ToIntXX (value) có thể giúp biên dịch "// Phần còn lại của mã" - ít nhất là cho đến khi kiểu trả về của IntegerFunction cũng thuộc loại T, sau đó bạn được đặt vòng tròn. :-p
yoyo

-1; điều này không hoạt động vì lý do được đưa ra bởi @Nick. Khoảnh khắc bạn cố gắng thực hiện bất kỳ thao tác số học nào // Rest of code...như value + valuehoặc value * value, bạn đã gặp lỗi biên dịch.
Đánh dấu Amery

13

Có lẽ gần nhất bạn có thể làm là

static bool IntegerFunction<T>(T value) where T: struct

Không chắc chắn nếu bạn có thể làm như sau

static bool IntegerFunction<T>(T value) where T: struct, IComparable
, IFormattable, IConvertible, IComparable<T>, IEquatable<T>

Đối với một cái gì đó rất cụ thể, tại sao không chỉ có quá tải cho từng loại, danh sách rất ngắn và nó có thể có ít bộ nhớ hơn.


6

Bắt đầu với C # 7.3, bạn có thể sử dụng xấp xỉ gần hơn - ràng buộc không được quản lý để xác định rằng tham số loại là loại không được quản lý không con trỏ, không null .

class SomeGeneric<T> where T : unmanaged
{
//...
}

Ràng buộc không được quản lý ngụ ý ràng buộc cấu trúc và không thể được kết hợp với các ràng buộc struct hoặc new ().

Một loại là một loại không được quản lý nếu đó là bất kỳ loại nào sau đây:

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, binary, hoặc bool
  • Bất kỳ loại enum
  • Bất kỳ loại con trỏ
  • Bất kỳ loại cấu trúc do người dùng xác định chỉ chứa các trường không được quản lý và trong C # 7.3 trở về trước, không phải là loại được xây dựng (một loại bao gồm ít nhất một đối số loại)

Để hạn chế hơn nữa và loại bỏ con trỏ và các loại do người dùng xác định không triển khai IComparable add IComparable (nhưng enum vẫn có nguồn gốc từ IComparable, vì vậy hãy hạn chế enum bằng cách thêm IEquitable <T>, bạn có thể đi xa hơn tùy theo hoàn cảnh của mình và thêm giao diện bổ sung. không được quản lý cho phép giữ danh sách này ngắn hơn):

    class SomeGeneric<T> where T : unmanaged, IComparable, IEquatable<T>
    {
    //...
    }

Đẹp, nhưng chưa đủ ... Ví dụ, DateTimerơi vào sự unmanaged, IComparable, IEquatable<T>ràng buộc ..
Adam Calvet Bohl

Tôi biết nhưng bạn có thể đi xa hơn tùy thuộc vào hoàn cảnh của bạn và thêm giao diện bổ sung. không được quản lý cho phép giữ danh sách này ngắn hơn. tôi vừa chỉ ra cách tiếp cận, gần đúng bằng cách sử dụng không được quản lý. Đối với hầu hết các trường hợp, điều này là đủ
Vlad Novakovsky

4

Không có cách nào để hạn chế các mẫu cho các loại, nhưng bạn có thể xác định các hành động khác nhau dựa trên loại. Là một phần của gói số chung, tôi cần một lớp chung để thêm hai giá trị.

    class Something<TCell>
    {
        internal static TCell Sum(TCell first, TCell second)
        {
            if (typeof(TCell) == typeof(int))
                return (TCell)((object)(((int)((object)first)) + ((int)((object)second))));

            if (typeof(TCell) == typeof(double))
                return (TCell)((object)(((double)((object)first)) + ((double)((object)second))));

            return second;
        }
    }

Lưu ý rằng các kiểu chữ được đánh giá tại thời điểm biên dịch, vì vậy các câu lệnh if sẽ bị trình biên dịch loại bỏ. Trình biên dịch cũng loại bỏ các phôi giả. Vì vậy, một cái gì đó sẽ giải quyết trong trình biên dịch để

        internal static int Sum(int first, int second)
        {
            return first + second;
        }

Cảm ơn bạn đã cung cấp một giải pháp thực nghiệm!
zsf222

Nó không giống với việc tạo ra cùng một phương thức cho mỗi loại?
Luis

3

Tôi đã tạo một chức năng thư viện nhỏ để giải quyết các vấn đề này:

Thay vì:

public T DifficultCalculation<T>(T a, T b)
{
    T result = a * b + a; // <== WILL NOT COMPILE!
    return result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Should result in 8.

Bạn có thể viết:

public T DifficultCalculation<T>(Number<T> a, Number<T> b)
{
    Number<T> result = a * b + a;
    return (T)result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Results in 8.

Bạn có thể tìm thấy mã nguồn tại đây: /codereview/26022/improference-requested-for-generic-calculator-and-generic-number


2

Tôi đã tự hỏi giống như samjudson, tại sao chỉ để số nguyên? và nếu đó là trường hợp, bạn có thể muốn tạo một lớp trợ giúp hoặc một cái gì đó tương tự để giữ tất cả các loại bạn muốn.

Nếu tất cả những gì bạn muốn là số nguyên, đừng sử dụng chung, đó không phải là chung; hoặc tốt hơn nữa, từ chối bất kỳ loại nào khác bằng cách kiểm tra loại của nó.


2

Không có giải pháp "tốt" nào cho việc này. Tuy nhiên, bạn có thể thu hẹp đáng kể đối số loại để loại trừ nhiều lỗi cho các ràng buộc 'INumeric' giả định của bạn như Haacked đã trình bày ở trên.

Hàm số nguyên bool tĩnh <T> (giá trị T) trong đó T: IComparable, IFormattable, IConvertible, IComparable <T>, IEquitable <T>, struct {...


2

Nếu bạn đang sử dụng .NET 4.0 trở lên thì bạn chỉ có thể sử dụng động làm đối số phương thức và kiểm tra trong thời gian chạy rằng loại đối số động được truyền là loại số / số nguyên.

Nếu loại động được truyền không phải là kiểu số / số nguyên thì ném ngoại lệ.

Một ví dụ mã ngắn thực hiện ý tưởng là một cái gì đó như:

using System;
public class InvalidArgumentException : Exception
{
    public InvalidArgumentException(string message) : base(message) {}
}
public class InvalidArgumentTypeException : InvalidArgumentException
{
    public InvalidArgumentTypeException(string message) : base(message) {}
}
public class ArgumentTypeNotIntegerException : InvalidArgumentTypeException
{
    public ArgumentTypeNotIntegerException(string message) : base(message) {}
}
public static class Program
{
    private static bool IntegerFunction(dynamic n)
    {
        if (n.GetType() != typeof(Int16) &&
            n.GetType() != typeof(Int32) &&
            n.GetType() != typeof(Int64) &&
            n.GetType() != typeof(UInt16) &&
            n.GetType() != typeof(UInt32) &&
            n.GetType() != typeof(UInt64))
            throw new ArgumentTypeNotIntegerException("argument type is not integer type");
        //code that implements IntegerFunction goes here
    }
    private static void Main()
    {
         Console.WriteLine("{0}",IntegerFunction(0)); //Compiles, no run time error and first line of output buffer is either "True" or "False" depends on the code that implements "Program.IntegerFunction" static method.
         Console.WriteLine("{0}",IntegerFunction("string")); //Also compiles but it is run time error and exception of type "ArgumentTypeNotIntegerException" is thrown here.
         Console.WriteLine("This is the last Console.WriteLine output"); //Never reached and executed due the run time error and the exception thrown on the second line of Program.Main static method.
    }

Tất nhiên là giải pháp này chỉ hoạt động trong thời gian chạy nhưng không bao giờ trong thời gian biên dịch.

Nếu bạn muốn một giải pháp luôn hoạt động trong thời gian biên dịch và không bao giờ trong thời gian chạy thì bạn sẽ phải kết hợp động với một cấu trúc / lớp công khai có công khai quá tải xây dựng chấp nhận các đối số của các kiểu mong muốn và đặt tên thích hợp cho lớp / lớp.

Điều có nghĩa là động năng bao bọc luôn là thành viên riêng của lớp / struct và nó là thành viên duy nhất của struct / class và tên của thành viên duy nhất của struct / class là "value".

Bạn cũng sẽ phải xác định và triển khai các phương thức công khai và / hoặc toán tử hoạt động với các kiểu mong muốn cho thành viên động riêng của lớp / struct nếu cần.

Cũng có nghĩa là struct / class có hàm tạo đặc biệt / duy nhất chấp nhận động như là đối số khởi tạo thành viên động riêng tư duy nhất gọi là "value" nhưng dĩ nhiên công cụ sửa đổi của hàm tạo này là riêng tư .

Khi lớp / struct đã sẵn sàng, hãy xác định kiểu IntegerFunction của đối số là lớp / struct đó đã được xác định.

Một ví dụ mã dài thực hiện ý tưởng là một cái gì đó như:

using System;
public struct Integer
{
    private dynamic value;
    private Integer(dynamic n) { this.value = n; }
    public Integer(Int16 n) { this.value = n; }
    public Integer(Int32 n) { this.value = n; }
    public Integer(Int64 n) { this.value = n; }
    public Integer(UInt16 n) { this.value = n; }
    public Integer(UInt32 n) { this.value = n; }
    public Integer(UInt64 n) { this.value = n; }
    public Integer(Integer n) { this.value = n.value; }
    public static implicit operator Int16(Integer n) { return n.value; }
    public static implicit operator Int32(Integer n) { return n.value; }
    public static implicit operator Int64(Integer n) { return n.value; }
    public static implicit operator UInt16(Integer n) { return n.value; }
    public static implicit operator UInt32(Integer n) { return n.value; }
    public static implicit operator UInt64(Integer n) { return n.value; }
    public static Integer operator +(Integer x, Int16 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, Int32 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, Int64 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt16 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt32 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt64 y) { return new Integer(x.value + y); }
    public static Integer operator -(Integer x, Int16 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, Int32 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, Int64 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt16 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt32 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt64 y) { return new Integer(x.value - y); }
    public static Integer operator *(Integer x, Int16 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, Int32 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, Int64 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt16 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt32 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt64 y) { return new Integer(x.value * y); }
    public static Integer operator /(Integer x, Int16 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, Int32 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, Int64 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt16 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt32 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt64 y) { return new Integer(x.value / y); }
    public static Integer operator %(Integer x, Int16 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, Int32 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, Int64 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt16 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt32 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt64 y) { return new Integer(x.value % y); }
    public static Integer operator +(Integer x, Integer y) { return new Integer(x.value + y.value); }
    public static Integer operator -(Integer x, Integer y) { return new Integer(x.value - y.value); }
    public static Integer operator *(Integer x, Integer y) { return new Integer(x.value * y.value); }
    public static Integer operator /(Integer x, Integer y) { return new Integer(x.value / y.value); }
    public static Integer operator %(Integer x, Integer y) { return new Integer(x.value % y.value); }
    public static bool operator ==(Integer x, Int16 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int16 y) { return x.value != y; }
    public static bool operator ==(Integer x, Int32 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int32 y) { return x.value != y; }
    public static bool operator ==(Integer x, Int64 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int64 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt16 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt16 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt32 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt32 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt64 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt64 y) { return x.value != y; }
    public static bool operator ==(Integer x, Integer y) { return x.value == y.value; }
    public static bool operator !=(Integer x, Integer y) { return x.value != y.value; }
    public override bool Equals(object obj) { return this == (Integer)obj; }
    public override int GetHashCode() { return this.value.GetHashCode(); }
    public override string ToString() { return this.value.ToString(); }
    public static bool operator >(Integer x, Int16 y) { return x.value > y; }
    public static bool operator <(Integer x, Int16 y) { return x.value < y; }
    public static bool operator >(Integer x, Int32 y) { return x.value > y; }
    public static bool operator <(Integer x, Int32 y) { return x.value < y; }
    public static bool operator >(Integer x, Int64 y) { return x.value > y; }
    public static bool operator <(Integer x, Int64 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt16 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt16 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt32 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt32 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt64 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt64 y) { return x.value < y; }
    public static bool operator >(Integer x, Integer y) { return x.value > y.value; }
    public static bool operator <(Integer x, Integer y) { return x.value < y.value; }
    public static bool operator >=(Integer x, Int16 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int16 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Int32 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int32 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Int64 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int64 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt16 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt16 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt32 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt32 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt64 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt64 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Integer y) { return x.value >= y.value; }
    public static bool operator <=(Integer x, Integer y) { return x.value <= y.value; }
    public static Integer operator +(Int16 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(Int32 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(Int64 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt16 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt32 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt64 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator -(Int16 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(Int32 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(Int64 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt16 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt32 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt64 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator *(Int16 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(Int32 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(Int64 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt16 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt32 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt64 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator /(Int16 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(Int32 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(Int64 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt16 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt32 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt64 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator %(Int16 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(Int32 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(Int64 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt16 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt32 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt64 x, Integer y) { return new Integer(x % y.value); }
    public static bool operator ==(Int16 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int16 x, Integer y) { return x != y.value; }
    public static bool operator ==(Int32 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int32 x, Integer y) { return x != y.value; }
    public static bool operator ==(Int64 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int64 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt16 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt16 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt32 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt32 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt64 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt64 x, Integer y) { return x != y.value; }
    public static bool operator >(Int16 x, Integer y) { return x > y.value; }
    public static bool operator <(Int16 x, Integer y) { return x < y.value; }
    public static bool operator >(Int32 x, Integer y) { return x > y.value; }
    public static bool operator <(Int32 x, Integer y) { return x < y.value; }
    public static bool operator >(Int64 x, Integer y) { return x > y.value; }
    public static bool operator <(Int64 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt16 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt16 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt32 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt32 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt64 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt64 x, Integer y) { return x < y.value; }
    public static bool operator >=(Int16 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int16 x, Integer y) { return x <= y.value; }
    public static bool operator >=(Int32 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int32 x, Integer y) { return x <= y.value; }
    public static bool operator >=(Int64 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int64 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt16 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt16 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt32 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt32 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt64 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt64 x, Integer y) { return x <= y.value; }
}
public static class Program
{
    private static bool IntegerFunction(Integer n)
    {
        //code that implements IntegerFunction goes here
        //note that there is NO code that checks the type of n in rum time, because it is NOT needed anymore 
    }
    private static void Main()
    {
        Console.WriteLine("{0}",IntegerFunction(0)); //compile error: there is no overloaded METHOD for objects of type "int" and no implicit conversion from any object, including "int", to "Integer" is known.
        Console.WriteLine("{0}",IntegerFunction(new Integer(0))); //both compiles and no run time error
        Console.WriteLine("{0}",IntegerFunction("string")); //compile error: there is no overloaded METHOD for objects of type "string" and no implicit conversion from any object, including "string", to "Integer" is known.
        Console.WriteLine("{0}",IntegerFunction(new Integer("string"))); //compile error: there is no overloaded CONSTRUCTOR for objects of type "string"
    }
}

Lưu ý rằng để sử dụng động trong mã của bạn, bạn phải Thêm tham chiếu vào Microsoft.CSharp

Nếu phiên bản của .NET framework ở dưới / dưới / nhỏ hơn 4.0 và động không được xác định trong phiên bản đó thì bạn sẽ phải sử dụng đối tượng và thực hiện chuyển sang loại số nguyên, vì vậy tôi khuyên bạn nên sử dụng tại Ít nhất là .NET 4.0 hoặc mới hơn nếu bạn có thể để bạn có thể sử dụng động thay vì đối tượng .


2

Thật không may .NET không cung cấp một cách để làm điều đó một cách tự nhiên.

Để giải quyết vấn đề này, tôi đã tạo ra thư viện OSS Genumerics cung cấp hầu hết các hoạt động số tiêu chuẩn cho các loại số tích hợp sau và các tương đương không thể bỏ qua của chúng với khả năng thêm hỗ trợ cho các loại số khác.

sbyte, byte, short, ushort, int,uint , long, ulong, float, double, decimal, VàBigInteger

Hiệu suất tương đương với một giải pháp cụ thể kiểu số cho phép bạn tạo các thuật toán số chung hiệu quả.

Đây là một ví dụ về việc sử dụng mã.

public static T Sum(T[] items)
{
    T sum = Number.Zero<T>();
    foreach (T item in items)
    {
        sum = Number.Add(sum, item);
    }
    return sum;
}
public static T SumAlt(T[] items)
{
    // implicit conversion to Number<T>
    Number<T> sum = Number.Zero<T>();
    foreach (T item in items)
    {
        // operator support
        sum += item;
    }
    // implicit conversion to T
    return sum;
}

1

Điểm của bài tập là gì?

Như mọi người đã chỉ ra, bạn có thể có một hàm không chung chung lấy mục lớn nhất và trình biên dịch sẽ tự động chuyển đổi các int nhỏ hơn cho bạn.

static bool IntegerFunction(Int64 value) { }

Nếu chức năng của bạn nằm trên đường dẫn quan trọng về hiệu năng (rất khó xảy ra, IMO), bạn có thể cung cấp quá tải cho tất cả các chức năng cần thiết.

static bool IntegerFunction(Int64 value) { }
...
static bool IntegerFunction(Int16 value) { }

1
Tôi làm việc với các phương pháp số rất nhiều. Đôi khi tôi muốn số nguyên và đôi khi tôi muốn dấu phẩy động. Cả hai đều có phiên bản 64 bit tối ưu cho tốc độ xử lý. Chuyển đổi giữa những điều này là một ý tưởng khủng khiếp vì có những mất mát mỗi cách. Mặc dù tôi có xu hướng sử dụng gấp đôi, đôi khi tôi thấy sử dụng số nguyên tốt hơn vì cách chúng được sử dụng ở nơi khác. Nhưng sẽ rất tuyệt khi tôi viết một thuật toán để thực hiện nó một lần và để lại quyết định loại theo yêu cầu cá thể.
VoteCoffee

1

Tôi sẽ sử dụng một cái chung mà bạn có thể xử lý bên ngoài ...

/// <summary>
/// Generic object copy of the same type
/// </summary>
/// <typeparam name="T">The type of object to copy</typeparam>
/// <param name="ObjectSource">The source object to copy</param>
public T CopyObject<T>(T ObjectSource)
{
    T NewObject = System.Activator.CreateInstance<T>();

    foreach (PropertyInfo p in ObjectSource.GetType().GetProperties())
        NewObject.GetType().GetProperty(p.Name).SetValue(NewObject, p.GetValue(ObjectSource, null), null);

    return NewObject;
}

1

Giới hạn này ảnh hưởng đến tôi khi tôi cố gắng quá tải toán tử cho các loại chung; vì không có ràng buộc "INumeric" và vì một số lý do khác mà những người tốt trên stackoverflow sẵn lòng cung cấp, các hoạt động không thể được định nghĩa trên các loại chung.

Tôi muốn một cái gì đó như

public struct Foo<T>
{
    public T Value{ get; private set; }

    public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
    {
        return new Foo<T> { Value = LHS.Value + RHS.Value; };
    }
}

Tôi đã giải quyết vấn đề này bằng cách sử dụng kiểu gõ động .net4.

public struct Foo<T>
{
    public T Value { get; private set; }

    public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
    {
        return new Foo<T> { Value = LHS.Value + (dynamic)RHS.Value };
    }
}

Hai điều về việc sử dụng dynamic

  1. Hiệu suất. Tất cả các loại giá trị được đóng hộp.
  2. Lỗi thời gian chạy. Bạn "đánh bại" trình biên dịch, nhưng mất an toàn kiểu. Nếu loại chung không có toán tử được xác định, một ngoại lệ sẽ được đưa ra trong khi thực thi.

1

Các kiểu nguyên thủy số .NET không chia sẻ bất kỳ giao diện chung nào cho phép chúng được sử dụng để tính toán. Nó sẽ có thể để xác định các giao diện riêng của bạn (ví dụ ISignedWholeNumber) mà sẽ thực hiện các hoạt động đó, xác định cấu trúc có chứa một đĩa đơn Int16, Int32vv và thực hiện các giao diện này, và sau đó có phương pháp mà chấp nhận các loại generic hạn chế đến ISignedWholeNumber, nhưng cần phải chuyển đổi các giá trị số các loại cấu trúc của bạn có thể sẽ gây phiền toái.

Một phương pháp khác sẽ được xác định lớp tĩnh Int64Converter<T>với một tài sản tĩnh bool Available {get;};và các đại biểu tĩnh cho Int64 GetInt64(T value), T FromInt64(Int64 value), bool TryStoreInt64(Int64 value, ref T dest). Trình xây dựng lớp có thể sử dụng được mã hóa cứng để tải các đại biểu cho các kiểu đã biết và có thể sử dụng Reflection để kiểm tra xem kiểu đó có Tthực hiện các phương thức với tên và chữ ký thích hợp hay không (trong trường hợp đó là một cấu trúc có chứa Int64và đại diện cho một số, nhưng một ToString()phương pháp tùy chỉnh ). Cách tiếp cận này sẽ mất đi những lợi thế liên quan đến kiểm tra kiểu thời gian biên dịch, nhưng vẫn có thể tránh được các hoạt động đấm bốc và mỗi loại chỉ phải được "kiểm tra" một lần. Sau đó, các hoạt động liên quan đến loại đó sẽ được thay thế bằng một công văn ủy nhiệm.


@KenKin: IConvertible cung cấp một phương tiện theo đó bất kỳ số nguyên nào cũng có thể được thêm vào một loại số nguyên khác để mang lại Int64kết quả, nhưng không cung cấp một phương tiện, ví dụ như một số nguyên của loại tùy ý có thể được tăng lên để mang lại một số nguyên khác cùng loại .
supercat

1

Tôi đã có một tình huống tương tự khi tôi cần xử lý các kiểu số và chuỗi; có vẻ một chút pha trộn kỳ lạ nhưng có bạn đi.

Một lần nữa, giống như nhiều người tôi đã xem xét các ràng buộc và đưa ra một loạt các giao diện mà nó phải hỗ trợ. Tuy nhiên, a) nó không kín nước 100% và b), bất kỳ ai mới nhìn vào danh sách dài hạn này sẽ bị nhầm lẫn ngay lập tức.

Vì vậy, cách tiếp cận của tôi là đưa tất cả logic của tôi vào một phương thức chung không có ràng buộc, nhưng làm cho phương thức chung đó trở nên riêng tư. Sau đó, tôi đã tiếp xúc nó với các phương thức công khai, một cách xử lý rõ ràng loại tôi muốn xử lý - theo tôi, mã này rõ ràng và rõ ràng, ví dụ:

public static string DoSomething(this int input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this decimal input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this double input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this string input, ...) => DoSomethingHelper(input, ...);

private static string DoSomethingHelper<T>(this T input, ....)
{
    // complex logic
}

0

Nếu tất cả những gì bạn muốn là sử dụng một loại số , bạn có thể xem xét việc tạo một cái gì đó tương tự như bí danh trong C ++ với using.

Vì vậy, thay vì có rất chung chung

T ComputeSomething<T>(T value1, T value2) where T : INumeric { ... }

bạn có thể có

using MyNumType = System.Double;
T ComputeSomething<MyNumType>(MyNumType value1, MyNumType value2) { ... }

Điều đó có thể cho phép bạn dễ dàng đi từ doubleđến inthoặc người khác nếu cần, nhưng bạn sẽ không thể sử dụng ComputeSomethingvới doubleint trong cùng một chương trình.

Nhưng tại sao không thay thế tất cả doubleđể intsau đó? Bởi vì phương pháp của bạn có thể muốn sử dụng doublecho dù đầu vào là doublehay int. Bí danh cho phép bạn biết chính xác biến nào sử dụng loại động .


0

Chủ đề đã cũ nhưng dành cho độc giả tương lai:

Tính năng này liên quan chặt chẽ đến tính năng Discriminated Unionskhông được triển khai trong C # cho đến nay. Tôi tìm thấy vấn đề của nó ở đây:

https://github.com/dotnet/csharplang/issues/113

Vấn đề này vẫn đang mở và tính năng đã được lên kế hoạch cho C# 10

Vì vậy, chúng tôi vẫn phải chờ thêm một chút, nhưng sau khi phát hành, bạn có thể làm theo cách này:

static bool IntegerFunction<T>(T value) where T : Int16 | Int32 | Int64 | ...

-11

Tôi nghĩ rằng bạn đang hiểu lầm chung chung. Nếu thao tác bạn đang cố thực hiện chỉ tốt cho các loại dữ liệu cụ thể thì bạn không làm điều gì đó "chung chung".

Ngoài ra, vì bạn chỉ muốn cho phép chức năng hoạt động trên các loại dữ liệu int nên bạn không cần một chức năng riêng cho từng kích thước cụ thể. Chỉ cần lấy một tham số trong loại cụ thể lớn nhất sẽ cho phép chương trình tự động upcast các loại dữ liệu nhỏ hơn cho nó. (tức là chuyển một Int16 sẽ tự động chuyển đổi sang Int64 khi gọi).

Nếu bạn đang thực hiện các hoạt động khác nhau dựa trên kích thước thực tế của int được truyền vào hàm thì tôi nghĩ bạn nên nghiêm túc xem xét lại ngay cả khi cố gắng làm những gì bạn đang làm. Nếu bạn phải đánh lừa ngôn ngữ, bạn nên suy nghĩ thêm một chút về những gì bạn đang cố gắng thực hiện thay vì làm thế nào để làm những gì bạn muốn.

Không thể sử dụng tất cả các loại khác, một tham số loại Object có thể được sử dụng và sau đó bạn sẽ phải kiểm tra loại tham số và thực hiện hành động thích hợp hoặc đưa ra một ngoại lệ.


10
Hãy xem xét một biểu đồ lớp <T>. Thật hợp lý khi để nó lấy tham số chung, vì vậy trình biên dịch có thể tối ưu hóa nó cho byte, ints, double, binary, BigInt, ... nhưng đồng thời bạn cần ngăn chặn rằng bạn có thể tạo một biểu đồ, giả sử, Biểu đồ <Hashset >, bởi vì - nói chuyện với Tron - nó không tính toán. (nghĩa đen :))
sunside

15
Bạn là người hiểu sai về thuốc generic. Metaprogramming không chỉ hoạt động trên các giá trị có thể là bất kỳ loại nào có thể , nó hoạt động trên các loại phù hợp với các ràng buộc khác nhau .
Jim Balter
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.